From 61b2237257dedec38960fd09491b45d98b1caf99 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 23 Aug 2020 19:49:21 +0200 Subject: [PATCH 001/385] Removed dependency. --- .gitignore | 1 - src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 025815d8b..381422d9b 100644 --- a/.gitignore +++ b/.gitignore @@ -213,7 +213,6 @@ ModelManifest.xml # Strong-Name Key *.snk -/src/Cuemon.Core/Cuemon.Core.csproj.DotSettings # SonarLint .sonarlint/ diff --git a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj index 270f62836..a882b1574 100644 --- a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj +++ b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj @@ -35,7 +35,7 @@ - + \ No newline at end of file From daa3cac9bc760165d2e0f133bd2a69aa8c4ed913 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 24 Aug 2020 01:53:34 +0200 Subject: [PATCH 002/385] Fixed to work on ASPNET Core 3. --- .../Filters/Diagnostics/HttpRequestEvidence.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/HttpRequestEvidence.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/HttpRequestEvidence.cs index 01ebcac1e..dcb683a3b 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/HttpRequestEvidence.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/HttpRequestEvidence.cs @@ -11,7 +11,6 @@ namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics /// public class HttpRequestEvidence { - // TODO: REMEMBER TO TEST THIS THOROUGHLY ON ASPNET CORE 3 internal HttpRequestEvidence(HttpRequest request, Func bodyParser = null) { var hasMultipartContentType = request.GetMultipartBoundary().Length > 0; @@ -23,7 +22,9 @@ internal HttpRequestEvidence(HttpRequest request, Func bodyParse if (request.HasFormContentType && !hasMultipartContentType) { Form = request.Form; } Cookies = request.Cookies; #if NETCOREAPP - Body = bodyParser(request.BodyReader.AsStream(true)); + var requestBody = new MemoryStream(); + Decorator.Enclose(request.BodyReader.AsStream(true)).CopyStream(requestBody); + Body = bodyParser(requestBody); #else Body = bodyParser(request.Body); #endif From daae506eab88fa03872f09b47d413e20030233fc Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 24 Aug 2020 02:11:19 +0200 Subject: [PATCH 003/385] Dependency changed to LTS version prior to NET Core 3. --- .../Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj | 2 +- src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj b/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj index 590579815..841d8dbfb 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj +++ b/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj @@ -30,7 +30,7 @@ - + diff --git a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj index a882b1574..f21901399 100644 --- a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj +++ b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj @@ -30,7 +30,7 @@ - + From af72277c58efd6d55f8a1e4cc019ba47fdab04db Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 24 Aug 2020 02:12:34 +0200 Subject: [PATCH 004/385] Dependency changed to LTS version prior to NET Core 3. --- .../Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj b/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj index b00d076d2..bc81f3265 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj +++ b/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj @@ -30,7 +30,7 @@ - + From 46ca2544421cfa09cb1b87731909b4235d4e58d4 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 24 Aug 2020 02:14:07 +0200 Subject: [PATCH 005/385] Revert "Dependency changed to LTS version prior to NET Core 3." This reverts commit af72277c58efd6d55f8a1e4cc019ba47fdab04db. --- .../Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj b/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj index bc81f3265..b00d076d2 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj +++ b/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj @@ -30,7 +30,7 @@ - + From e754e981d21e3eab458c366173671149dfc5e694 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 24 Aug 2020 02:14:25 +0200 Subject: [PATCH 006/385] Revert "Dependency changed to LTS version prior to NET Core 3." This reverts commit daae506eab88fa03872f09b47d413e20030233fc. --- .../Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj | 2 +- src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj b/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj index 841d8dbfb..590579815 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj +++ b/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj @@ -30,7 +30,7 @@ - + diff --git a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj index f21901399..a882b1574 100644 --- a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj +++ b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj @@ -30,7 +30,7 @@ - + From e33a6b5c5fcd495018d1f5240cac9536b1236545 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 24 Aug 2020 20:07:57 +0200 Subject: [PATCH 007/385] Compliant with both ASP NET Core 2 and 3. Had to store a captured request body because of the new BodyReader in 3. --- ...ions.AspNetCore.Mvc.Formatters.Json.csproj | 2 +- .../JsonSerializationInputFormatter.cs | 20 +++++++++++++------ ...sions.AspNetCore.Mvc.Formatters.Xml.csproj | 2 +- .../XmlSerializationInputFormatter.cs | 20 +++++++++++++------ .../Diagnostics/FaultDescriptorFilter.cs | 7 ++++++- .../Diagnostics/HttpRequestEvidence.cs | 16 +++++++-------- 6 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj b/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj index 590579815..7752e7fcf 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj +++ b/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj @@ -34,7 +34,7 @@ - + diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationInputFormatter.cs b/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationInputFormatter.cs index 40fb17982..6ec8f62a3 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationInputFormatter.cs +++ b/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationInputFormatter.cs @@ -1,5 +1,7 @@ -using System.Text; +using System.IO; +using System.Text; using System.Threading.Tasks; +using Cuemon.AspNetCore.Mvc.Filters.Diagnostics; using Cuemon.Extensions.Newtonsoft.Json.Formatters; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Formatters; @@ -30,15 +32,21 @@ public JsonSerializationInputFormatter(JsonFormatterOptions formatterOptions) /// The . /// The used to read the request body. /// A that on completion deserializes the request body. - public override Task ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) + public override async Task ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) { Validator.ThrowIfNull(context, nameof(context)); Validator.ThrowIfNull(encoding, nameof(encoding)); - context.HttpContext.Request.EnableBuffering(); + var requestBody = new MemoryStream(); + #if NETCOREAPP + await context.HttpContext.Request.BodyReader.CopyToAsync(requestBody).ConfigureAwait(false); + #else + await context.HttpContext.Request.Body.CopyToAsync(requestBody).ConfigureAwait(false); + #endif + requestBody.Position = 0; var formatter = new JsonFormatter(FormatterOptions); - var deserializedObject = formatter.Deserialize(context.HttpContext.Request.Body, context.ModelType); - context.HttpContext.Request.Body.Position = 0; - return InputFormatterResult.SuccessAsync(deserializedObject); + var deserializedObject = formatter.Deserialize(requestBody, context.ModelType); + context.HttpContext.Items.Add(FaultDescriptorFilter.HttpContextItemsKeyForCapturedRequestBody, requestBody); + return await InputFormatterResult.SuccessAsync(deserializedObject).ConfigureAwait(false); } private JsonFormatterOptions FormatterOptions { get; } diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj b/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj index b00d076d2..5ab7b4eac 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj +++ b/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj @@ -34,7 +34,7 @@ - + diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlSerializationInputFormatter.cs b/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlSerializationInputFormatter.cs index bed6bba4d..72c92b9ac 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlSerializationInputFormatter.cs +++ b/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlSerializationInputFormatter.cs @@ -1,5 +1,7 @@ -using System.Text; +using System.IO; +using System.Text; using System.Threading.Tasks; +using Cuemon.AspNetCore.Mvc.Filters.Diagnostics; using Cuemon.Xml.Serialization.Formatters; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Formatters; @@ -30,15 +32,21 @@ public XmlSerializationInputFormatter(XmlFormatterOptions formatterOptions) /// The . /// The used to read the request body. /// A that on completion deserializes the request body. - public override Task ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) + public override async Task ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) { Validator.ThrowIfNull(context, nameof(context)); Validator.ThrowIfNull(encoding, nameof(encoding)); - context.HttpContext.Request.EnableBuffering(); + var requestBody = new MemoryStream(); + #if NETCOREAPP + await context.HttpContext.Request.BodyReader.CopyToAsync(requestBody).ConfigureAwait(false); + #else + await context.HttpContext.Request.Body.CopyToAsync(requestBody).ConfigureAwait(false); + #endif + requestBody.Position = 0; var formatter = new XmlFormatter(FormatterOptions); - var deserializedObject = formatter.Deserialize(context.HttpContext.Request.Body, context.ModelType); - context.HttpContext.Request.Body.Position = 0; - return InputFormatterResult.SuccessAsync(deserializedObject); + var deserializedObject = formatter.Deserialize(requestBody, context.ModelType); + context.HttpContext.Items.Add(FaultDescriptorFilter.HttpContextItemsKeyForCapturedRequestBody, requestBody); + return await InputFormatterResult.SuccessAsync(deserializedObject).ConfigureAwait(false); } private XmlFormatterOptions FormatterOptions { get; } diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorFilter.cs index dbe45c613..d077d5f27 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorFilter.cs @@ -21,6 +21,11 @@ namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics /// public class FaultDescriptorFilter : Configurable, IExceptionFilter { + /// + /// The key to set or get a copy of a captured request body. + /// + public const string HttpContextItemsKeyForCapturedRequestBody = "CuemonAspNetCoreMvcFiltersDiagnostics_HttpContextItemsKeyForCapturedRequestBody"; + /// /// Initializes a new instance of the class. /// @@ -32,7 +37,7 @@ public FaultDescriptorFilter(IOptions setup) : base(setu /// /// Called after an action has thrown an . /// - /// The . + /// The . public virtual void OnException(ExceptionContext context) { if (context.ActionDescriptor is ControllerActionDescriptor actionDescriptor) diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/HttpRequestEvidence.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/HttpRequestEvidence.cs index dcb683a3b..3418b1b63 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/HttpRequestEvidence.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/HttpRequestEvidence.cs @@ -11,23 +11,23 @@ namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics /// public class HttpRequestEvidence { - internal HttpRequestEvidence(HttpRequest request, Func bodyParser = null) + internal HttpRequestEvidence(HttpRequest request, Func bodyConverter = null) { var hasMultipartContentType = request.GetMultipartBoundary().Length > 0; - if (bodyParser == null) { bodyParser = body => hasMultipartContentType ? null : Decorator.Enclose(body).ToEncodedString(); } + if (bodyConverter == null) { bodyConverter = body => hasMultipartContentType ? null : Decorator.Enclose(body).ToEncodedString(); } Location = request.GetDisplayUrl(); Method = request.Method; Headers = request.Headers; Query = request.Query; if (request.HasFormContentType && !hasMultipartContentType) { Form = request.Form; } Cookies = request.Cookies; - #if NETCOREAPP var requestBody = new MemoryStream(); - Decorator.Enclose(request.BodyReader.AsStream(true)).CopyStream(requestBody); - Body = bodyParser(requestBody); - #else - Body = bodyParser(request.Body); - #endif + if (request.HttpContext.Items.TryGetValue(FaultDescriptorFilter.HttpContextItemsKeyForCapturedRequestBody, out var capturedRequestBody) && capturedRequestBody is MemoryStream crb) + { + crb.Position = 0; + requestBody = crb; + } + Body = bodyConverter(requestBody); } /// From 347553af23f60b36b1d4d90bb7c5c82e1e981653 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 24 Aug 2020 20:10:25 +0200 Subject: [PATCH 008/385] Fix to support models with default ctor. --- .../Serialization/Converters/DefaultXmlConverter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Cuemon.Xml/Serialization/Converters/DefaultXmlConverter.cs b/src/Cuemon.Xml/Serialization/Converters/DefaultXmlConverter.cs index 8d51a81f5..9b3a19e33 100644 --- a/src/Cuemon.Xml/Serialization/Converters/DefaultXmlConverter.cs +++ b/src/Cuemon.Xml/Serialization/Converters/DefaultXmlConverter.cs @@ -189,6 +189,7 @@ private object ParseReadXmlDefault(XmlReader reader, Type valueType) } } + var hasDefaultCtor = false; var constructors = valueType.GetConstructors(new MemberReflection(excludeStatic: true)).ToList(); var properties = valueType.GetProperties(new MemberReflection(excludeStatic: true)).Where(info => info.CanWrite).ToDictionary(info => info.Name); var propertyNames = properties.Select(info => info.Key).Intersect(values.Select(pair => pair.Key), StringComparer.OrdinalIgnoreCase).ToList(); @@ -200,6 +201,7 @@ private object ParseReadXmlDefault(XmlReader reader, Type valueType) var argumentsLength = arguments.Select(info => info.Name).Intersect(values.Select(pair => pair.Key), StringComparer.OrdinalIgnoreCase).Count(); if (arguments.Length == argumentsLength) { + if (!hasDefaultCtor && argumentsLength == 0) { hasDefaultCtor = true; } foreach (var arg in arguments) { args.Add(Decorator.Enclose(values.First(pair => pair.Key.Equals(arg.Name, StringComparison.OrdinalIgnoreCase)).Value).ChangeType(arg.ParameterType)); @@ -224,7 +226,7 @@ private object ParseReadXmlDefault(XmlReader reader, Type valueType) return method.Invoke(null, args.ToArray()); } } - throw new SerializationException("Unable to find a suitable constructor or static method for deserialization."); + if (!hasDefaultCtor) { throw new SerializationException("Unable to find a suitable constructor or static method for deserialization."); } } var instance = Activator.CreateInstance(valueType, args.ToArray()); From 69ef6397266f66987c6628f007a6e75c04eed945 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 24 Aug 2020 23:32:46 +0200 Subject: [PATCH 009/385] Refactored with Bootstrapper and added consistency with more modern code. --- .../XmlMvcCoreBuilderExtensions.cs | 89 ------------------- .../Bootstrapper.cs | 35 ++++++++ .../Converters/XmlConverterExtensions.cs | 13 --- ...sions.AspNetCore.Mvc.Formatters.Xml.csproj | 4 +- .../MvcBuilderExtensions.cs | 72 +++++++++++++++ .../MvcCoreBuilderExtensions.cs | 72 +++++++++++++++ .../Properties/AssemblyInfo.cs | 0 .../XmlSerializationInputFormatter.cs | 1 - .../XmlSerializationMvcOptionsSetup.cs | 0 .../XmlSerializationOutputFormatter.cs | 0 .../Formatters/XmlFormatterOptions.cs | 2 +- 11 files changed, 182 insertions(+), 106 deletions(-) delete mode 100644 src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlMvcCoreBuilderExtensions.cs create mode 100644 src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Bootstrapper.cs rename src/{Cuemon.AspNetCore.Mvc.Formatters.Xml => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml}/Converters/XmlConverterExtensions.cs (94%) rename src/{Cuemon.AspNetCore.Mvc.Formatters.Xml => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml}/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj (92%) create mode 100644 src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/MvcBuilderExtensions.cs create mode 100644 src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/MvcCoreBuilderExtensions.cs rename src/{Cuemon.AspNetCore.Mvc.Formatters.Xml => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml}/Properties/AssemblyInfo.cs (100%) rename src/{Cuemon.AspNetCore.Mvc.Formatters.Xml => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml}/XmlSerializationInputFormatter.cs (98%) rename src/{Cuemon.AspNetCore.Mvc.Formatters.Xml => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml}/XmlSerializationMvcOptionsSetup.cs (100%) rename src/{Cuemon.AspNetCore.Mvc.Formatters.Xml => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml}/XmlSerializationOutputFormatter.cs (100%) diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlMvcCoreBuilderExtensions.cs b/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlMvcCoreBuilderExtensions.cs deleted file mode 100644 index 6e6ff3299..000000000 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlMvcCoreBuilderExtensions.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using Cuemon.Collections.Generic; -using Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters; -using Cuemon.Xml.Serialization.Formatters; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; - -namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml -{ - /// - /// Extension methods for adding XML formatters to MVC. - /// - public static class XmlMvcCoreBuilderExtensions - { - /// - /// Adds the XML Serializer formatters to MVC. - /// - /// The . - /// The . - public static IMvcCoreBuilder AddXmlSerializationFormatters(this IMvcCoreBuilder builder) - { - Validator.ThrowIfNull(builder, nameof(builder)); - builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, XmlSerializationMvcOptionsSetup>()); - return builder; - } - - /// - /// Adds the XML Serializer formatters to MVC. - /// - /// The . - /// The . - public static IMvcBuilder AddXmlSerializationFormatters(this IMvcBuilder builder) - { - Validator.ThrowIfNull(builder, nameof(builder)); - builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, XmlSerializationMvcOptionsSetup>()); - return builder; - } - - /// - /// Adds configuration of for the application. - /// - /// The . - /// The which need to be configured. - /// The . - public static IMvcCoreBuilder AddXmlFormatterOptions(this IMvcCoreBuilder builder, Action setup) - { - Validator.ThrowIfNull(builder, nameof(builder)); - Validator.ThrowIfNull(setup, nameof(setup)); - builder.Services.Configure(DefaultXmlFormatterOptions(setup)); - return builder; - } - - /// - /// Adds configuration of for the application. - /// - /// The . - /// The which need to be configured. - /// The . - public static IMvcBuilder AddXmlFormatterOptions(this IMvcBuilder builder, Action setup) - { - Validator.ThrowIfNull(builder, nameof(builder)); - Validator.ThrowIfNull(setup, nameof(setup)); - builder.Services.Configure(DefaultXmlFormatterOptions(setup)); - return builder; - } - - private static Action DefaultXmlFormatterOptions(Action setup) - { - var options = Patterns.Configure(setup); - return o => - { - o.IncludeExceptionStackTrace = options.IncludeExceptionStackTrace; - o.SynchronizeWithXmlConvert = options.SynchronizeWithXmlConvert; - o.Settings.Writer = options.Settings.Writer; - o.Settings.RootName = options.Settings.RootName; - o.Settings.Reader = options.Settings.Reader; - Decorator.Enclose(o.Settings.Converters).AddRange(options.Settings.Converters); - o.Settings.Converters.AddStringValuesConverter(); - o.Settings.Converters.AddHeaderDictionaryConverter(); - o.Settings.Converters.AddQueryCollectionConverter(); - o.Settings.Converters.AddFormCollectionConverter(); - o.Settings.Converters.AddCookieCollectionConverter(); - o.Settings.Converters.AddHttpExceptionDescriptorConverter(); - }; - } - } -} \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Bootstrapper.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Bootstrapper.cs new file mode 100644 index 000000000..8332804f9 --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Bootstrapper.cs @@ -0,0 +1,35 @@ +using Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters; +using Cuemon.Xml.Serialization; + +namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml +{ + internal static class Bootstrapper + { + private static readonly object PadLock = new object(); + private static bool _initialized = false; + + internal static void Initialize() + { + if (!_initialized) + { + lock (PadLock) + { + if (!_initialized) + { + _initialized = true; + XmlSerializerOptions.DefaultConverters += list => + { + list.AddHttpExceptionDescriptorConverter() + .AddStringValuesConverter() + .AddHeaderDictionaryConverter() + .AddFormCollectionConverter() + .AddQueryCollectionConverter() + .AddCookieCollectionConverter(); + }; + } + } + + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs similarity index 94% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs index 22fc9539c..126458c53 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs @@ -18,19 +18,6 @@ namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters /// public static class XmlConverterExtensions { - static XmlConverterExtensions() - { - XmlSerializerOptions.DefaultConverters += list => - { - list.AddHttpExceptionDescriptorConverter() - .AddStringValuesConverter() - .AddHeaderDictionaryConverter() - .AddFormCollectionConverter() - .AddQueryCollectionConverter() - .AddCookieCollectionConverter(); - }; - } - /// /// Adds an XML converter to the list. /// diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj similarity index 92% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj index 5ab7b4eac..7bb3ffed9 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj @@ -9,10 +9,10 @@ - Cuemon .NET Standard + Cuemon Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml - The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml assembly is a serialization supplement to Microsoft ASP.NET Core. + The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml assembly provides extension methods and XML formatters for ASP.NET Core MVC. Geekle Michael Mortensen Copyright © Geekle 2009-2020. All rights reserved. diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/MvcBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/MvcBuilderExtensions.cs new file mode 100644 index 000000000..8a423d27e --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/MvcBuilderExtensions.cs @@ -0,0 +1,72 @@ +using System; +using Cuemon.Xml.Serialization.Formatters; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; + +namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml +{ + /// + /// Extension methods for the interface. + /// + public static class MvcBuilderExtensions + { + static MvcBuilderExtensions() + { + Bootstrapper.Initialize(); + } + + /// + /// Adds the XML serializer formatters to MVC. + /// + /// The to extend. + /// A reference to after the operation has completed. + /// + /// cannot be null. + /// + public static IMvcBuilder AddXmlSerializationFormatters(this IMvcBuilder builder) + { + Validator.ThrowIfNull(builder, nameof(builder)); + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, XmlSerializationMvcOptionsSetup>()); + return builder; + } + + /// + /// Adds the XML serializer formatters to MVC. + /// + /// The to extend. + /// The which need to be configured. + /// A reference to after the operation has completed. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IMvcBuilder AddXmlSerializationFormatters(this IMvcBuilder builder, Action setup) + { + Validator.ThrowIfNull(builder, nameof(builder)); + AddXmlSerializationFormatters(builder); + AddXmlFormatterOptions(builder, setup); + return builder; + } + + + /// + /// Adds configuration of for the application. + /// + /// The to extend. + /// The which need to be configured. + /// A reference to after the operation has completed. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IMvcBuilder AddXmlFormatterOptions(this IMvcBuilder builder, Action setup) + { + Validator.ThrowIfNull(builder, nameof(builder)); + Validator.ThrowIfNull(setup, nameof(setup)); + builder.Services.Configure(setup); + return builder; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/MvcCoreBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/MvcCoreBuilderExtensions.cs new file mode 100644 index 000000000..7c6ba3f9d --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/MvcCoreBuilderExtensions.cs @@ -0,0 +1,72 @@ +using System; +using Cuemon.Xml.Serialization.Formatters; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; + +namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml +{ + /// + /// Extension methods for the interface. + /// + public static class MvcCoreBuilderExtensions + { + static MvcCoreBuilderExtensions() + { + Bootstrapper.Initialize(); + } + + /// + /// Adds the XML serializer formatters to MVC. + /// + /// The . + /// A reference to after the operation has completed. + /// + /// cannot be null. + /// + public static IMvcCoreBuilder AddXmlSerializationFormatters(this IMvcCoreBuilder builder) + { + Validator.ThrowIfNull(builder, nameof(builder)); + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, XmlSerializationMvcOptionsSetup>()); + return builder; + } + + /// + /// Adds the XML serializer formatters to MVC. + /// + /// The . + /// The which need to be configured. + /// A reference to after the operation has completed. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IMvcCoreBuilder AddXmlSerializationFormatters(this IMvcCoreBuilder builder, Action setup) + { + Validator.ThrowIfNull(builder, nameof(builder)); + AddXmlSerializationFormatters(builder); + AddXmlFormatterOptions(builder, setup); + return builder; + } + + /// + /// Adds configuration of for the application. + /// + /// The . + /// The which need to be configured. + /// A reference to after the operation has completed. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IMvcCoreBuilder AddXmlFormatterOptions(this IMvcCoreBuilder builder, Action setup) + { + Validator.ThrowIfNull(builder, nameof(builder)); + Validator.ThrowIfNull(setup, nameof(setup)); + AddXmlSerializationFormatters(builder); + builder.Services.Configure(setup); + return builder; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Properties/AssemblyInfo.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Properties/AssemblyInfo.cs similarity index 100% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Xml/Properties/AssemblyInfo.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Properties/AssemblyInfo.cs diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlSerializationInputFormatter.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/XmlSerializationInputFormatter.cs similarity index 98% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlSerializationInputFormatter.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/XmlSerializationInputFormatter.cs index 72c92b9ac..28eaec2bf 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlSerializationInputFormatter.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/XmlSerializationInputFormatter.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Cuemon.AspNetCore.Mvc.Filters.Diagnostics; using Cuemon.Xml.Serialization.Formatters; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Net.Http.Headers; diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlSerializationMvcOptionsSetup.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/XmlSerializationMvcOptionsSetup.cs similarity index 100% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlSerializationMvcOptionsSetup.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/XmlSerializationMvcOptionsSetup.cs diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlSerializationOutputFormatter.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/XmlSerializationOutputFormatter.cs similarity index 100% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Xml/XmlSerializationOutputFormatter.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/XmlSerializationOutputFormatter.cs diff --git a/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs b/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs index c78abf5c2..666f2ce57 100644 --- a/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs +++ b/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs @@ -46,7 +46,7 @@ public XmlFormatterOptions() IncludeExceptionDescriptorFailure = true; IncludeExceptionDescriptorEvidence = true; IncludeExceptionStackTrace = false; - XmlSerializerOptions.DefaultConverters = list => + XmlSerializerOptions.DefaultConverters += list => { Decorator.Enclose(list) .AddExceptionDescriptorConverter() From 0b03d83536fdbeba24c3edc2df9859620cc17b9e Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 25 Aug 2020 00:28:51 +0200 Subject: [PATCH 010/385] Adjusted to new namespace to be consistent with adapted naming convention. Also mimicking same setup as XML converters with a default delegate for setting up converters and bootstrap. --- Cuemon.sln | 4 +- .../DefaultJsonSerializerSettings.cs | 62 ---------------- .../Bootstrapper.cs | 31 ++++++++ .../JsonConverterCollectionExtensions.cs | 2 +- ...ore.Mvc.Formatters.Newtonsoft.Json.csproj} | 8 +-- .../JsonSerializationInputFormatter.cs | 3 +- .../JsonSerializationMvcOptionsSetup.cs | 2 +- .../JsonSerializationOutputFormatter.cs | 2 +- .../JsonSerializerSettingsExtensions.cs | 2 +- .../MvcBuilderExtensions.cs | 71 +++++++++++++++++++ .../MvcCoreBuilderExtensions.cs} | 52 +++++++------- .../Properties/AssemblyInfo.cs | 0 .../Formatters/JsonFormatterOptions.cs | 29 +++++--- 13 files changed, 160 insertions(+), 108 deletions(-) delete mode 100644 src/Cuemon.AspNetCore.Mvc.Formatters.Json/DefaultJsonSerializerSettings.cs create mode 100644 src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Bootstrapper.cs rename src/{Cuemon.AspNetCore.Mvc.Formatters.Json => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json}/Converters/JsonConverterCollectionExtensions.cs (98%) rename src/{Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj} (80%) rename src/{Cuemon.AspNetCore.Mvc.Formatters.Json => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json}/JsonSerializationInputFormatter.cs (96%) rename src/{Cuemon.AspNetCore.Mvc.Formatters.Json => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json}/JsonSerializationMvcOptionsSetup.cs (92%) rename src/{Cuemon.AspNetCore.Mvc.Formatters.Json => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json}/JsonSerializationOutputFormatter.cs (97%) rename src/{Cuemon.AspNetCore.Mvc.Formatters.Json => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json}/JsonSerializerSettingsExtensions.cs (97%) create mode 100644 src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcBuilderExtensions.cs rename src/{Cuemon.AspNetCore.Mvc.Formatters.Json/JsonMvcCoreBuilderExtensions.cs => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcCoreBuilderExtensions.cs} (57%) rename src/{Cuemon.AspNetCore.Mvc.Formatters.Json => Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json}/Properties/AssemblyInfo.cs (100%) diff --git a/Cuemon.sln b/Cuemon.sln index 1108aa31f..cb21f6df5 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -13,9 +13,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.AspNetCore", "src\Cu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Newtonsoft.Json", "src\Cuemon.Extensions.Newtonsoft.Json\Cuemon.Extensions.Newtonsoft.Json.csproj", "{080BDF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml", "src\Cuemon.AspNetCore.Mvc.Formatters.Xml\Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj", "{A70ADF91-E7C7-4CB4-A39D-E1A5374C5602}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml", "src\Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml\Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj", "{A70ADF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json", "src\Cuemon.AspNetCore.Mvc.Formatters.Json\Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj", "{A60ADF91-E7C7-4CB4-A39D-E1A5374C5602}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json", "src\Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json\Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj", "{A60ADF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.IO", "src\Cuemon.Extensions.IO\Cuemon.Extensions.IO.csproj", "{060BDF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/DefaultJsonSerializerSettings.cs b/src/Cuemon.AspNetCore.Mvc.Formatters.Json/DefaultJsonSerializerSettings.cs deleted file mode 100644 index 205de9af8..000000000 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/DefaultJsonSerializerSettings.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using Cuemon.Diagnostics; -using Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.Converters; -using Cuemon.Extensions.Newtonsoft.Json.Converters; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json -{ - /// - /// Specifies the default settings on a object as interpreted by this framework. - /// - public class DefaultJsonSerializerSettings : JsonSerializerSettings - { - /// - /// Initializes a new instance of the class. - /// - public DefaultJsonSerializerSettings() - { - IncludeExceptionDescriptorFailure = true; - IncludeExceptionDescriptorEvidence = true; - IncludeExceptionStackTrace = false; - Formatting = Formatting.Indented; - NullValueHandling = NullValueHandling.Ignore; - MissingMemberHandling = MissingMemberHandling.Ignore; - ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - DateParseHandling = DateParseHandling.DateTimeOffset; - DateFormatHandling = DateFormatHandling.IsoDateFormat; - DateTimeZoneHandling = DateTimeZoneHandling.Utc; - ContractResolver = new CamelCasePropertyNamesContractResolver(); - Converters.AddStringValuesConverter() - .AddHttpExceptionDescriptorConverter(o => - { - o.IncludeEvidence = IncludeExceptionDescriptorEvidence; - o.IncludeFailure = IncludeExceptionDescriptorFailure; - }) - .AddExceptionConverter(() => IncludeExceptionStackTrace) - .AddDataPairConverter() - .AddStringEnumConverter() - .AddStringFlagsEnumConverter() - .AddTimeSpanConverter(); - } - - /// - /// Gets or sets a value indicating whether the stack of an is included in the converter that handles exceptions. - /// - /// true if the stack of an is included in the converter that handles exceptions; otherwise, false. - public bool IncludeExceptionStackTrace { get; set; } - - /// - /// Gets or sets a value indicating whether the failure of an is included in the converter that handles exception descriptors. - /// - /// true if the failure of an is included in the converter that handles exception descriptors; otherwise, false. - public bool IncludeExceptionDescriptorFailure { get; set; } - - /// - /// Gets or sets a value indicating whether the evidence of an is included in the converter that handles exception descriptors. - /// - /// true if the evidence of an is included in the converter that handles exception descriptors; otherwise, false. - public bool IncludeExceptionDescriptorEvidence { get; set; } - } -} \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Bootstrapper.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Bootstrapper.cs new file mode 100644 index 000000000..27ace59ee --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Bootstrapper.cs @@ -0,0 +1,31 @@ +using Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters; +using Cuemon.Extensions.Newtonsoft.Json.Formatters; + +namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json +{ + internal static class Bootstrapper + { + private static readonly object PadLock = new object(); + private static bool _initialized = false; + + internal static void Initialize() + { + if (!_initialized) + { + lock (PadLock) + { + if (!_initialized) + { + _initialized = true; + JsonFormatterOptions.DefaultConverters += list => + { + list.AddHttpExceptionDescriptorConverter() + .AddStringValuesConverter(); + }; + } + } + + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Converters/JsonConverterCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs similarity index 98% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Json/Converters/JsonConverterCollectionExtensions.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs index 9c63ce0b2..520580e17 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Converters/JsonConverterCollectionExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Primitives; using Newtonsoft.Json; -namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.Converters +namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters { /// /// Extension methods for the class. diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj similarity index 80% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj index 7752e7fcf..69a8a988c 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj @@ -9,10 +9,10 @@ - Cuemon .NET Standard - Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json - Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json - The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json assembly is a serialization supplement to Microsoft ASP.NET Core. + Cuemon + Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json + Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json + The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json assembly provides extension methods and JSON formatters for ASP.NET Core MVC that uses the Newtonsoft.Json package. Geekle Michael Mortensen Copyright © Geekle 2009-2020. All rights reserved. diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationInputFormatter.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializationInputFormatter.cs similarity index 96% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationInputFormatter.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializationInputFormatter.cs index 6ec8f62a3..1b9f678f6 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationInputFormatter.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializationInputFormatter.cs @@ -3,11 +3,10 @@ using System.Threading.Tasks; using Cuemon.AspNetCore.Mvc.Filters.Diagnostics; using Cuemon.Extensions.Newtonsoft.Json.Formatters; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Net.Http.Headers; -namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json +namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json { /// /// This class handles deserialization of JSON to objects using . diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationMvcOptionsSetup.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializationMvcOptionsSetup.cs similarity index 92% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationMvcOptionsSetup.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializationMvcOptionsSetup.cs index b96c65aa5..aef5f4dc6 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationMvcOptionsSetup.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializationMvcOptionsSetup.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; -namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json +namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json { /// /// A implementation which will add the JSON serializer formatters to . diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationOutputFormatter.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializationOutputFormatter.cs similarity index 97% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationOutputFormatter.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializationOutputFormatter.cs index f769b41c8..26246af1e 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializationOutputFormatter.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializationOutputFormatter.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Net.Http.Headers; -namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json +namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json { /// /// This class handles serialization of objects to JSON using . diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializerSettingsExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializerSettingsExtensions.cs similarity index 97% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializerSettingsExtensions.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializerSettingsExtensions.cs index 1520b8a2b..ed8d8368b 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonSerializerSettingsExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/JsonSerializerSettingsExtensions.cs @@ -1,7 +1,7 @@ using System; using Newtonsoft.Json; -namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json +namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json { /// /// Extension methods for the class. diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcBuilderExtensions.cs new file mode 100644 index 000000000..68fcd48ff --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcBuilderExtensions.cs @@ -0,0 +1,71 @@ +using System; +using Cuemon.Extensions.Newtonsoft.Json.Formatters; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; + +namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json +{ + /// + /// Extension methods for the interface. + /// + public static class MvcBuilderExtensions + { + static MvcBuilderExtensions() + { + Bootstrapper.Initialize(); + } + + /// + /// Adds the JSON serializer formatters to MVC. + /// + /// The . + /// A reference to after the operation has completed. + /// + /// cannot be null. + /// + public static IMvcBuilder AddJsonSerializationFormatters(this IMvcBuilder builder) + { + Validator.ThrowIfNull(builder, nameof(builder)); + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, JsonSerializationMvcOptionsSetup>()); + return builder; + } + + /// + /// Adds the JSON serializer formatters to MVC. + /// + /// The . + /// The which need to be configured. + /// A reference to after the operation has completed. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IMvcBuilder AddJsonSerializationFormatters(this IMvcBuilder builder, Action setup) + { + Validator.ThrowIfNull(builder, nameof(builder)); + AddJsonSerializationFormatters(builder); + AddJsonFormatterOptions(builder, setup); + return builder; + } + + /// + /// Adds configuration of for the application. + /// + /// The . + /// The which need to be configured. + /// A reference to after the operation has completed. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IMvcBuilder AddJsonFormatterOptions(this IMvcBuilder builder, Action setup) + { + Validator.ThrowIfNull(builder, nameof(builder)); + Validator.ThrowIfNull(setup, nameof(setup)); + builder.Services.Configure(setup); + return builder; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonMvcCoreBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcCoreBuilderExtensions.cs similarity index 57% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonMvcCoreBuilderExtensions.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcCoreBuilderExtensions.cs index 216046f24..4be397e3d 100644 --- a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/JsonMvcCoreBuilderExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcCoreBuilderExtensions.cs @@ -5,31 +5,27 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Json +namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json { /// - /// Extension methods for adding JSON formatters to MVC. + /// Extension methods for the interface. /// - public static class JsonMvcCoreBuilderExtensions + public static class MvcCoreBuilderExtensions { - /// - /// Adds the JSON Serializer formatters to MVC. - /// - /// The . - /// The . - public static IMvcCoreBuilder AddJsonSerializationFormatters(this IMvcCoreBuilder builder) + static MvcCoreBuilderExtensions() { - Validator.ThrowIfNull(builder, nameof(builder)); - builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, JsonSerializationMvcOptionsSetup>()); - return builder; + Bootstrapper.Initialize(); } /// - /// Adds the JSON Serializer formatters to MVC. + /// Adds the JSON serializer formatters to MVC. /// - /// The . - /// The . - public static IMvcBuilder AddJsonSerializationFormatters(this IMvcBuilder builder) + /// The . + /// A reference to after the operation has completed. + /// + /// cannot be null. + /// + public static IMvcCoreBuilder AddJsonSerializationFormatters(this IMvcCoreBuilder builder) { Validator.ThrowIfNull(builder, nameof(builder)); builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, JsonSerializationMvcOptionsSetup>()); @@ -37,16 +33,18 @@ public static IMvcBuilder AddJsonSerializationFormatters(this IMvcBuilder builde } /// - /// Adds configuration of for the application. + /// Adds the JSON serializer formatters to MVC. /// - /// The . + /// The . /// The which need to be configured. - /// The . - public static IMvcCoreBuilder AddJsonFormatterOptions(this IMvcCoreBuilder builder, Action setup) + /// A reference to after the operation has completed. + /// + /// cannot be null. + /// + public static IMvcCoreBuilder AddJsonSerializationFormatters(this IMvcCoreBuilder builder, Action setup) { Validator.ThrowIfNull(builder, nameof(builder)); - Validator.ThrowIfNull(setup, nameof(setup)); - builder.Services.Configure(setup); + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, JsonSerializationMvcOptionsSetup>()); return builder; } @@ -55,8 +53,12 @@ public static IMvcCoreBuilder AddJsonFormatterOptions(this IMvcCoreBuilder build /// /// The . /// The which need to be configured. - /// The . - public static IMvcBuilder AddJsonFormatterOptions(this IMvcBuilder builder, Action setup) + /// A reference to after the operation has completed. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IMvcCoreBuilder AddJsonFormatterOptions(this IMvcCoreBuilder builder, Action setup) { Validator.ThrowIfNull(builder, nameof(builder)); Validator.ThrowIfNull(setup, nameof(setup)); @@ -64,4 +66,4 @@ public static IMvcBuilder AddJsonFormatterOptions(this IMvcBuilder builder, Acti return builder; } } -} \ No newline at end of file +} diff --git a/src/Cuemon.AspNetCore.Mvc.Formatters.Json/Properties/AssemblyInfo.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Properties/AssemblyInfo.cs similarity index 100% rename from src/Cuemon.AspNetCore.Mvc.Formatters.Json/Properties/AssemblyInfo.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Properties/AssemblyInfo.cs diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Formatters/JsonFormatterOptions.cs b/src/Cuemon.Extensions.Newtonsoft.Json/Formatters/JsonFormatterOptions.cs index 56686b7a2..44014fe3f 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/Formatters/JsonFormatterOptions.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Formatters/JsonFormatterOptions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Cuemon.Diagnostics; using Cuemon.Extensions.Newtonsoft.Json.Converters; using Newtonsoft.Json; @@ -60,18 +61,28 @@ public JsonFormatterOptions() DateTimeZoneHandling = DateTimeZoneHandling.Utc, ContractResolver = new CamelCasePropertyNamesContractResolver() }; - Settings.Converters.AddStringFlagsEnumConverter(); - Settings.Converters.AddStringEnumConverter(); - Settings.Converters.AddExceptionConverter(() => IncludeExceptionStackTrace); - Settings.Converters.AddExceptionDescriptorConverter(o => + DefaultConverters += list => { - o.IncludeEvidence = IncludeExceptionDescriptorEvidence; - o.IncludeFailure = IncludeExceptionDescriptorFailure; - }); - Settings.Converters.AddTimeSpanConverter(); - Settings.Converters.AddDataPairConverter(); + list.AddStringFlagsEnumConverter(); + list.AddStringEnumConverter(); + list.AddExceptionConverter(() => IncludeExceptionStackTrace); + list.AddExceptionDescriptorConverter(o => + { + o.IncludeEvidence = IncludeExceptionDescriptorEvidence; + o.IncludeFailure = IncludeExceptionDescriptorFailure; + }); + list.AddTimeSpanConverter(); + list.AddDataPairConverter(); + }; + DefaultConverters?.Invoke(Settings.Converters); } + /// + /// Gets or sets a delegate that is invoked when is initialized and propagates registered implementations. + /// + /// The delegate which propagates registered implementations when is initialized. + public static Action> DefaultConverters { get; set; } + /// /// Gets or sets a value indicating whether the stack of an is included in the converter that handles exceptions. /// From 57d5d20605566ee12644f5cc8d09c6c45f13a60d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 25 Aug 2020 00:33:37 +0200 Subject: [PATCH 011/385] Adjusted hardcoded count of classes in test. --- .../Reflection/AssemblyDecoratorExtensionsTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs index 1170067d7..b6aa8884e 100644 --- a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs @@ -36,7 +36,7 @@ public void GetTypes_ShouldReturnAllTypesFromCuemonCore() var disposableTypesCount = Decorator.Enclose(disposableTypes).Inner.Count(); var configurationTypesCount = Decorator.Enclose(configurationTypes).Inner.Count(); - Assert.Equal(532, allTypesCount); + Assert.Equal(525, allTypesCount); Assert.Equal(7, disposableTypesCount); Assert.Equal(2, configurationTypesCount); } From 491ea1d7ba90a9b874ff6489a6def62bdaa5d3a9 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 26 Aug 2020 22:17:07 +0200 Subject: [PATCH 012/385] Changed text. --- src/Cuemon.Data/BulkCopyDataReader.cs | 37 +++++++-------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/src/Cuemon.Data/BulkCopyDataReader.cs b/src/Cuemon.Data/BulkCopyDataReader.cs index 22d29283c..27738071b 100644 --- a/src/Cuemon.Data/BulkCopyDataReader.cs +++ b/src/Cuemon.Data/BulkCopyDataReader.cs @@ -10,7 +10,7 @@ namespace Cuemon.Data { /// - /// Provides a way of copying an existing object implementing the interface to a filtered forward-only stream of rows that is mapped for bulk upload. This class cannot be inherited. + /// Provides a way of copying an existing object implementing the class to a filtered forward-only stream of rows that is mapped for bulk upload. This class cannot be inherited. /// public sealed class BulkCopyDataReader : DbDataReader { @@ -66,29 +66,20 @@ private void SetFields(IOrderedDictionary fields) /// /// The name of the column to find. /// The column with the specified name as an . - public override object this[string name] - { - get { return Fields[name]; } - } + public override object this[string name] => Fields[name]; /// /// Gets the column located at the specified index. /// /// The zero-based index of the column to get. /// The column located at the specified index as an . - public override object this[int i] - { - get { return Fields[i]; } - } + public override object this[int i] => Fields[i]; /// /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement. /// /// The records affected. - public override int RecordsAffected - { - get { return -1; } - } + public override int RecordsAffected => -1; private IOrderedDictionary Fields { get; set; } @@ -96,18 +87,13 @@ public override int RecordsAffected /// Gets a value that indicates whether this contains one or more rows. /// /// true if this instance has rows; otherwise, false. - public override bool HasRows { - get { return Reader.HasRows; } - } + public override bool HasRows => Reader.HasRows; /// /// Gets a value indicating whether the data reader is closed. /// /// true if this instance is closed; otherwise, false. - public override bool IsClosed - { - get { return IsDisposed; } - } + public override bool IsClosed => IsDisposed; private bool IsDisposed { get; set; } @@ -122,10 +108,8 @@ public override bool IsClosed /// Gets the number of columns in the current row. /// /// When not positioned in a valid recordset, 0; otherwise, the number of columns in the current record. - public override int FieldCount - { - get { return Mappings.Count; } - } + public override int FieldCount => Mappings.Count; + #endregion #region Methods @@ -449,10 +433,7 @@ protected override void Dispose(bool disposing) /// /// The depth of nesting for the current row. /// The outermost table has a depth of zero. - public override int Depth - { - get { return 0; } - } + public override int Depth => 0; /// /// Populates an array of objects with the column values of the current record. From 9e63dd79b6a9c02a1de321b085351d44908fa0f1 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 26 Aug 2020 23:01:29 +0200 Subject: [PATCH 013/385] First draft of automated semantic versioning. --- Directory.Build.props | 52 +++++++++++++++++++++++++++++++++++++++++++ version.json | 20 +++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 Directory.Build.props create mode 100644 version.json diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..53f7966ef --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,52 @@ + + + + $(MSBuildProjectName.EndsWith('Tests')) + + + + Copyright © Geekle 2009-2020. All rights reserved. + Michael Mortensen + Geekle + Cuemon + https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png + https://www.cuemon.net/ + MIT + https://github.com/gimlichael/CuemonCore + git + en-US + true + true + true + ..\cuemon.snk + true + + + + + + + + + netcoreapp3.0 + false + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + \ No newline at end of file diff --git a/version.json b/version.json new file mode 100644 index 000000000..8caed689e --- /dev/null +++ b/version.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "6.0.2020-preview.{height}", + "assemblyVersion": + { + "precision": "build" + }, + "publicReleaseRefSpec": [ + "^refs/heads/master$", + "^refs/heads/v\\d+(?:\\.\\d+)?$" + ], + "nugetPackageVersion": { + "semVer": 2 + }, + "cloudBuild": { + "buildNumber": { + "enabled": true + } + } +} \ No newline at end of file From 241dd3db3b8d57f5e1563e6d8ab6ad5a20d060cf Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 26 Aug 2020 23:03:42 +0200 Subject: [PATCH 014/385] Test of assembly versioning. --- version.json | 1 + 1 file changed, 1 insertion(+) diff --git a/version.json b/version.json index 8caed689e..231a3f6f7 100644 --- a/version.json +++ b/version.json @@ -3,6 +3,7 @@ "version": "6.0.2020-preview.{height}", "assemblyVersion": { + "version": "6.0.2020.25", "precision": "build" }, "publicReleaseRefSpec": [ From 45a8955366a1ba495128c3f47bf3b051c1d062f6 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 26 Aug 2020 23:04:19 +0200 Subject: [PATCH 015/385] Test --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 231a3f6f7..1fc8faf70 100644 --- a/version.json +++ b/version.json @@ -4,7 +4,7 @@ "assemblyVersion": { "version": "6.0.2020.25", - "precision": "build" + "precision": "revision" }, "publicReleaseRefSpec": [ "^refs/heads/master$", From 3097ba4b1e03507861c32453dae1bf19aef97be3 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 26 Aug 2020 23:04:54 +0200 Subject: [PATCH 016/385] Test --- version.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/version.json b/version.json index 1fc8faf70..dc016a22a 100644 --- a/version.json +++ b/version.json @@ -3,8 +3,7 @@ "version": "6.0.2020-preview.{height}", "assemblyVersion": { - "version": "6.0.2020.25", - "precision": "revision" + "version": "6.0.2020.25" }, "publicReleaseRefSpec": [ "^refs/heads/master$", From d5319bd8748efafcf57267a77bad92191f426682 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 26 Aug 2020 23:06:02 +0200 Subject: [PATCH 017/385] test --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index dc016a22a..5d3144082 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "6.0.2020-preview.{height}", + "version": "6.0.2020-preview", "assemblyVersion": { "version": "6.0.2020.25" From 6efa630bf4ba3349680c6ed848b653424eee034f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 26 Aug 2020 23:06:44 +0200 Subject: [PATCH 018/385] Test --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 5d3144082..d0da733ab 100644 --- a/version.json +++ b/version.json @@ -3,7 +3,7 @@ "version": "6.0.2020-preview", "assemblyVersion": { - "version": "6.0.2020.25" + "precision": "revision" }, "publicReleaseRefSpec": [ "^refs/heads/master$", From d7d91c12dfe6db84a13dfe27f7dcf2958900df32 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 26 Aug 2020 23:07:03 +0200 Subject: [PATCH 019/385] Test --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index d0da733ab..f60fefb36 100644 --- a/version.json +++ b/version.json @@ -3,7 +3,7 @@ "version": "6.0.2020-preview", "assemblyVersion": { - "precision": "revision" + "precision": "build" }, "publicReleaseRefSpec": [ "^refs/heads/master$", From 62ecbac0613780e02eb1deac07abd46b7b4bbe4a Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 26 Aug 2020 23:35:41 +0200 Subject: [PATCH 020/385] Test versioning. --- version.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/version.json b/version.json index f60fefb36..3a4050277 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "6.0.2020-preview", + "version": "6.0.0-preview.{height}", "assemblyVersion": { "precision": "build" @@ -11,10 +11,5 @@ ], "nugetPackageVersion": { "semVer": 2 - }, - "cloudBuild": { - "buildNumber": { - "enabled": true - } } } \ No newline at end of file From 45189f7547f71a2139151415a4aa6ff82ec29fd7 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 26 Aug 2020 23:46:30 +0200 Subject: [PATCH 021/385] Version playground. --- version.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.json b/version.json index 3a4050277..f9a6daef3 100644 --- a/version.json +++ b/version.json @@ -1,9 +1,9 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "6.0.0-preview.{height}", + "version": "6.0.0-preview.0", "assemblyVersion": { - "precision": "build" + "precision": "minor" }, "publicReleaseRefSpec": [ "^refs/heads/master$", From 10b33d4ad0084955475fdee7eda1c5158705c3ee Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 26 Aug 2020 23:54:14 +0200 Subject: [PATCH 022/385] Version fun --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index f9a6daef3..75a57abf6 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "6.0.0-preview.0", + "version": "6.0.0-prerelease.{height}", "assemblyVersion": { "precision": "minor" From 046bb4a8d863f2815aaeed4547f09da1bff9982f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 00:53:44 +0200 Subject: [PATCH 023/385] All unit test projects adjusted to use Directory.Build.props. --- .../Cuemon.AspNetCore.Mvc.Tests.csproj | 18 ------------- .../Cuemon.AspNetCore.Tests.csproj | 22 ++-------------- .../Cuemon.Core.Tests.csproj | 25 +------------------ .../AssemblyDecoratorExtensionsTest.cs | 15 ++++++++--- .../Cuemon.Data.Tests.csproj | 20 +-------------- .../Cuemon.Diagnostics.Tests.csproj | 18 ------------- .../Cuemon.Extensions.Core.Tests.csproj | 20 +-------------- ...Cuemon.Extensions.Diagnostics.Tests.csproj | 20 +-------------- ...mon.Extensions.Data.Integrity.Tests.csproj | 22 +--------------- .../Cuemon.Extensions.Net.Tests.csproj | 20 +-------------- .../Cuemon.Extensions.Xml.Tests.csproj | 20 +-------------- test/Cuemon.IO.Tests/Cuemon.IO.Tests.csproj | 18 ------------- test/Cuemon.Net.Tests/Cuemon.Net.Tests.csproj | 20 +-------------- .../Cuemon.Resilience.Tests.csproj | 20 +-------------- .../Cuemon.Threading.Tests.csproj | 20 ++------------- test/Cuemon.Xml.Tests/Cuemon.Xml.Tests.csproj | 20 +-------------- 16 files changed, 25 insertions(+), 293 deletions(-) diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj b/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj index abd7de891..a1120c533 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj @@ -1,29 +1,11 @@ - netcoreapp3.0 - - false - Cuemon.AspNetCore.Mvc - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj b/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj index 2b2ab47c5..8f83561f3 100644 --- a/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj +++ b/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj @@ -1,29 +1,11 @@ - + - netcoreapp3.1 - - false - Cuemon.AspNetCore - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + \ No newline at end of file diff --git a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj index 6d485cb89..838fce310 100644 --- a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj +++ b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj @@ -1,38 +1,15 @@ - netcoreapp3.0 - - false - Cuemon - - x64 - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - + \ No newline at end of file diff --git a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs index b6aa8884e..37c061fa1 100644 --- a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs @@ -36,7 +36,7 @@ public void GetTypes_ShouldReturnAllTypesFromCuemonCore() var disposableTypesCount = Decorator.Enclose(disposableTypes).Inner.Count(); var configurationTypesCount = Decorator.Enclose(configurationTypes).Inner.Count(); - Assert.Equal(525, allTypesCount); + Assert.Equal(528, allTypesCount); Assert.Equal(7, disposableTypesCount); Assert.Equal(2, configurationTypesCount); } @@ -46,7 +46,9 @@ public void GetAssemblyVersion_ShouldReturnAssemblyVersion() { var a = typeof(Disposable).Assembly; var v = Decorator.Enclose(a).GetAssemblyVersion(); - Assert.Equal("6.0.2020.0", v.ToString()); + Assert.Equal("6.0.0.0", v.ToString()); + Assert.True(v.HasAlphanumericVersion); + Assert.False(v.IsSemanticVersion()); } [Fact] @@ -54,7 +56,9 @@ public void GetFileVersion_ShouldReturnFileVersion() { var a = typeof(Disposable).Assembly; var v = Decorator.Enclose(a).GetFileVersion(); - Assert.Equal("6.0.2020.25", v.ToString()); + Assert.False(v.IsSemanticVersion()); + Assert.True(v.HasAlphanumericVersion); + Assert.Equal("6.0.0", v.ToString()); } [Fact] @@ -62,7 +66,10 @@ public void GetProductVersion_ShouldReturnProductVersion() { var a = typeof(Disposable).Assembly; var v = Decorator.Enclose(a).GetProductVersion(); - Assert.Equal("6.0.2020.25", v.ToString()); + Assert.True(v.IsSemanticVersion()); + Assert.True(v.HasAlphanumericVersion); + Assert.Equal("6.0", v.ToVersion().ToString()); + Assert.Contains("-prerelease", v.ToString()); } [Fact] diff --git a/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj b/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj index 9e03c0974..ec7ed8606 100644 --- a/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj +++ b/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj @@ -1,29 +1,11 @@ - netcoreapp3.1 - - false - Cuemon.Data - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + \ No newline at end of file diff --git a/test/Cuemon.Diagnostics.Tests/Cuemon.Diagnostics.Tests.csproj b/test/Cuemon.Diagnostics.Tests/Cuemon.Diagnostics.Tests.csproj index 075965305..15b7d82c5 100644 --- a/test/Cuemon.Diagnostics.Tests/Cuemon.Diagnostics.Tests.csproj +++ b/test/Cuemon.Diagnostics.Tests/Cuemon.Diagnostics.Tests.csproj @@ -1,29 +1,11 @@ - netcoreapp3.1 - - false - Cuemon.Diagnostics - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - \ No newline at end of file diff --git a/test/Cuemon.Extensions.Core.Tests/Cuemon.Extensions.Core.Tests.csproj b/test/Cuemon.Extensions.Core.Tests/Cuemon.Extensions.Core.Tests.csproj index 4acda19a8..2e950d328 100644 --- a/test/Cuemon.Extensions.Core.Tests/Cuemon.Extensions.Core.Tests.csproj +++ b/test/Cuemon.Extensions.Core.Tests/Cuemon.Extensions.Core.Tests.csproj @@ -1,29 +1,11 @@ - netcoreapp3.1 - - false - Cuemon.Extensions - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + \ No newline at end of file diff --git a/test/Cuemon.Extensions.Diagnostics.Tests/Cuemon.Extensions.Diagnostics.Tests.csproj b/test/Cuemon.Extensions.Diagnostics.Tests/Cuemon.Extensions.Diagnostics.Tests.csproj index 51ff59d46..413175ffd 100644 --- a/test/Cuemon.Extensions.Diagnostics.Tests/Cuemon.Extensions.Diagnostics.Tests.csproj +++ b/test/Cuemon.Extensions.Diagnostics.Tests/Cuemon.Extensions.Diagnostics.Tests.csproj @@ -1,29 +1,11 @@ - netcoreapp3.1 - - false - Cuemon.Extensions.Diagnostics - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + \ No newline at end of file diff --git a/test/Cuemon.Extensions.Integrity.Tests/Cuemon.Extensions.Data.Integrity.Tests.csproj b/test/Cuemon.Extensions.Integrity.Tests/Cuemon.Extensions.Data.Integrity.Tests.csproj index 572cbb288..de0895e5b 100644 --- a/test/Cuemon.Extensions.Integrity.Tests/Cuemon.Extensions.Data.Integrity.Tests.csproj +++ b/test/Cuemon.Extensions.Integrity.Tests/Cuemon.Extensions.Data.Integrity.Tests.csproj @@ -1,31 +1,11 @@ - netcoreapp3.1 - - false - - Cuemon.Extensions.Integrity.Tests - Cuemon.Extensions.Data.Integrity - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + \ No newline at end of file diff --git a/test/Cuemon.Extensions.Net.Tests/Cuemon.Extensions.Net.Tests.csproj b/test/Cuemon.Extensions.Net.Tests/Cuemon.Extensions.Net.Tests.csproj index 99fc921bf..6c47fd52f 100644 --- a/test/Cuemon.Extensions.Net.Tests/Cuemon.Extensions.Net.Tests.csproj +++ b/test/Cuemon.Extensions.Net.Tests/Cuemon.Extensions.Net.Tests.csproj @@ -1,30 +1,12 @@ - netcoreapp3.1 - - false - Cuemon.Extensions.Net - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + \ No newline at end of file diff --git a/test/Cuemon.Extensions.Xml.Tests/Cuemon.Extensions.Xml.Tests.csproj b/test/Cuemon.Extensions.Xml.Tests/Cuemon.Extensions.Xml.Tests.csproj index c070c845b..d48d6f5ec 100644 --- a/test/Cuemon.Extensions.Xml.Tests/Cuemon.Extensions.Xml.Tests.csproj +++ b/test/Cuemon.Extensions.Xml.Tests/Cuemon.Extensions.Xml.Tests.csproj @@ -1,10 +1,6 @@ - netcoreapp3.1 - - false - Cuemon.Extensions.Xml @@ -16,24 +12,10 @@ - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + \ No newline at end of file diff --git a/test/Cuemon.IO.Tests/Cuemon.IO.Tests.csproj b/test/Cuemon.IO.Tests/Cuemon.IO.Tests.csproj index 620288fd5..cbbf40002 100644 --- a/test/Cuemon.IO.Tests/Cuemon.IO.Tests.csproj +++ b/test/Cuemon.IO.Tests/Cuemon.IO.Tests.csproj @@ -1,28 +1,10 @@ - netcoreapp3.1 - - false - Cuemon.IO - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - diff --git a/test/Cuemon.Net.Tests/Cuemon.Net.Tests.csproj b/test/Cuemon.Net.Tests/Cuemon.Net.Tests.csproj index cce257874..98a5f894b 100644 --- a/test/Cuemon.Net.Tests/Cuemon.Net.Tests.csproj +++ b/test/Cuemon.Net.Tests/Cuemon.Net.Tests.csproj @@ -1,29 +1,11 @@ - netcoreapp3.1 - - false - Cuemon.Net - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + \ No newline at end of file diff --git a/test/Cuemon.Resilience.Tests/Cuemon.Resilience.Tests.csproj b/test/Cuemon.Resilience.Tests/Cuemon.Resilience.Tests.csproj index 2777644cf..7369aecc4 100644 --- a/test/Cuemon.Resilience.Tests/Cuemon.Resilience.Tests.csproj +++ b/test/Cuemon.Resilience.Tests/Cuemon.Resilience.Tests.csproj @@ -1,30 +1,12 @@ - netcoreapp3.1 - - false - Cuemon.Resilience - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/Cuemon.Threading.Tests.csproj b/test/Cuemon.Threading.Tests/Cuemon.Threading.Tests.csproj index 0563a73cb..7d527d707 100644 --- a/test/Cuemon.Threading.Tests/Cuemon.Threading.Tests.csproj +++ b/test/Cuemon.Threading.Tests/Cuemon.Threading.Tests.csproj @@ -1,27 +1,11 @@ - netcoreapp3.1 - - false + Cuemon.Threading - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + \ No newline at end of file diff --git a/test/Cuemon.Xml.Tests/Cuemon.Xml.Tests.csproj b/test/Cuemon.Xml.Tests/Cuemon.Xml.Tests.csproj index f393fe0bf..3e41733af 100644 --- a/test/Cuemon.Xml.Tests/Cuemon.Xml.Tests.csproj +++ b/test/Cuemon.Xml.Tests/Cuemon.Xml.Tests.csproj @@ -1,29 +1,11 @@ - netcoreapp3.1 - - false - Cuemon.Xml - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + \ No newline at end of file From af9af85aa82453816e346bf2d4bb9c7398e94448 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 00:54:28 +0200 Subject: [PATCH 024/385] In the transition to semantic versioning, current version method has been refactored to use new class VersionResult. --- .../Reflection/AssemblyDecoratorExtensions.cs | 24 ++--- src/Cuemon.Core/Reflection/VersionResult.cs | 95 +++++++++++++++++++ 2 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 src/Cuemon.Core/Reflection/VersionResult.cs diff --git a/src/Cuemon.Core/Extensions/Reflection/AssemblyDecoratorExtensions.cs b/src/Cuemon.Core/Extensions/Reflection/AssemblyDecoratorExtensions.cs index 4f69f4335..8017960e4 100644 --- a/src/Cuemon.Core/Extensions/Reflection/AssemblyDecoratorExtensions.cs +++ b/src/Cuemon.Core/Extensions/Reflection/AssemblyDecoratorExtensions.cs @@ -62,47 +62,47 @@ public static IEnumerable GetTypes(this IDecorator decorator, st } /// - /// Returns a that represents the of the underlying of the . + /// Returns a that represents the of the underlying of the . /// /// The to extend. - /// A that represents the underlying of the . + /// A that represents the underlying of the . /// /// cannot be null. /// - public static Version GetAssemblyVersion(this IDecorator decorator) + public static VersionResult GetAssemblyVersion(this IDecorator decorator) { Validator.ThrowIfNull(decorator, nameof(decorator)); - return decorator.Inner.GetName().Version; + return new VersionResult(decorator.Inner.GetName().Version); } /// - /// Returns a that represents the of the underlying of the . + /// Returns a that represents the of the underlying of the . /// /// The to extend. - /// A that represents the file version of the underlying of the ; null if no could be retrieved. + /// A that represents the file version of the underlying of the ; null if no could be retrieved. /// /// cannot be null. /// - public static Version GetFileVersion(this IDecorator decorator) + public static VersionResult GetFileVersion(this IDecorator decorator) { Validator.ThrowIfNull(decorator, nameof(decorator)); var version = decorator.Inner.GetCustomAttribute(); - return version == null ? null : new Version(version.Version); + return new VersionResult(version.Version); } /// - /// Returns a that represents the of the underlying of the . + /// Returns a that represents the of the underlying of the . /// /// The to extend. - /// A that represents the product version of the underlying of the ; null if no could be retrieved. + /// A that represents the product version of the underlying of the ; null if no could be retrieved. /// /// cannot be null. /// - public static Version GetProductVersion(this IDecorator decorator) + public static VersionResult GetProductVersion(this IDecorator decorator) { Validator.ThrowIfNull(decorator, nameof(decorator)); var version = decorator.Inner.GetCustomAttribute(); - return version == null ? null : new Version(version.InformationalVersion); + return new VersionResult(version.InformationalVersion); } /// diff --git a/src/Cuemon.Core/Reflection/VersionResult.cs b/src/Cuemon.Core/Reflection/VersionResult.cs new file mode 100644 index 000000000..f5c3fd108 --- /dev/null +++ b/src/Cuemon.Core/Reflection/VersionResult.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; + +namespace Cuemon.Reflection +{ + /// + /// Represents different representations of a version scheme in a consistent way. + /// + public class VersionResult + { + private readonly Version _version; + + /// + /// Initializes a new instance of the class. + /// + /// The that represents a potential alphanumeric version. + public VersionResult(string alphanumericVersion) + { + AlphanumericVersion = alphanumericVersion; + } + + /// + /// Initializes a new instance of the class. + /// + /// The that represents a numerical version {major.minor.build.revision}. + public VersionResult(Version version) + { + AlphanumericVersion = version?.ToString(); + _version = version; + } + + /// + /// Gets the alphanumeric version assigned to this instance. + /// + /// The alphanumeric version assigned to this instance. + public string AlphanumericVersion { get; } + + /// + /// Gets a value indicating whether this instance has alphanumeric version assigned. + /// + /// true if this instance has alphanumeric version assigned; otherwise, false. + public bool HasAlphanumericVersion => !string.IsNullOrEmpty(AlphanumericVersion); + + /// + /// Determines whether this instance represents a semantic version. + /// + /// true if this instance represents a semantic version; otherwise, false. + public bool IsSemanticVersion() + { + if (HasAlphanumericVersion) + { + var isSemantic = true; + var versions = AlphanumericVersion.Split('.'); + foreach (var version in versions) + { + isSemantic &= int.TryParse(version, out _); + } + return !isSemantic; + } + return false; + } + + /// + /// Converts this instance to an equivalent object. + /// + /// An equivalent object of this instance. + /// + /// Only a non-semantic version can be converted to a object. + /// + public Version ToVersion() + { + if (HasAlphanumericVersion && _version != null) { return _version; } + if (HasAlphanumericVersion) + { + var versionComponents = new List(); + var versions = AlphanumericVersion.Split('.'); + foreach (var version in versions) + { + if (int.TryParse(version, out var v)) { versionComponents.Add(v); } + } + return new Version(DelimitedString.Create(versionComponents, o => o.Delimiter = ".")); + } + throw new InvalidOperationException("Only a non-semantic version can be converted to a Version object."); + } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + return AlphanumericVersion; + } + } +} \ No newline at end of file From b69f4b56e166e6c6664d80a6d30b9b3e257f7ebf Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 00:54:53 +0200 Subject: [PATCH 025/385] Consequence changes after VersionResult. --- .../AssemblyExtensions.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Cuemon.Extensions.Reflection/AssemblyExtensions.cs b/src/Cuemon.Extensions.Reflection/AssemblyExtensions.cs index 663f4d735..bc961cf8e 100644 --- a/src/Cuemon.Extensions.Reflection/AssemblyExtensions.cs +++ b/src/Cuemon.Extensions.Reflection/AssemblyExtensions.cs @@ -10,37 +10,37 @@ namespace Cuemon.Extensions.Reflection public static class AssemblyExtensions { /// - /// Returns a that represents the version number of the specified . + /// Returns a that represents the version number of the specified . /// /// The assembly to resolve a from. - /// A that represents the version number of the specified . - public static Version GetAssemblyVersion(this Assembly assembly) + /// A that represents the version number of the specified . + public static VersionResult GetAssemblyVersion(this Assembly assembly) { return Decorator.Enclose(assembly).GetAssemblyVersion(); } /// - /// Returns a that represents the file version number of the specified . + /// Returns a that represents the file version number of the specified . /// /// The assembly to resolve a from. - /// A that represents the file version number of the specified . + /// A that represents the file version number of the specified . /// /// is null. /// - public static Version GetFileVersion(this Assembly assembly) + public static VersionResult GetFileVersion(this Assembly assembly) { return Decorator.Enclose(assembly).GetFileVersion(); } /// - /// Returns a that represents the version of the product this is distributed with. + /// Returns a that represents the version of the product this is distributed with. /// /// The assembly to resolve a from. - /// A that represents the version of the product this is distributed with. + /// A that represents the version of the product this is distributed with. /// /// is null. /// - public static Version GetProductVersion(this Assembly assembly) + public static VersionResult GetProductVersion(this Assembly assembly) { return Decorator.Enclose(assembly).GetProductVersion(); } From 7612edac78e5995a3c027308a51b63004cf84b0f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 00:55:08 +0200 Subject: [PATCH 026/385] Removed using. --- .../CacheableObjectResultExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/CacheableObjectResultExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/CacheableObjectResultExtensions.cs index 21d0899c3..89387ca7e 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/CacheableObjectResultExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/CacheableObjectResultExtensions.cs @@ -1,7 +1,6 @@ using System; using Cuemon.AspNetCore.Mvc; using Cuemon.AspNetCore.Mvc.Filters.Cacheable; -using Cuemon.Data; using Cuemon.Data.Integrity; namespace Cuemon.Extensions.AspNetCore.Mvc From dd4fcbb66fb72ad8b629b398c72e108b07f8d488 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 01:59:03 +0200 Subject: [PATCH 027/385] Moved to extensions assembly. --- .../HttpStatusCodeExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/{Cuemon.Net => Cuemon.Extensions.Net}/HttpStatusCodeExtensions.cs (99%) diff --git a/src/Cuemon.Net/HttpStatusCodeExtensions.cs b/src/Cuemon.Extensions.Net/HttpStatusCodeExtensions.cs similarity index 99% rename from src/Cuemon.Net/HttpStatusCodeExtensions.cs rename to src/Cuemon.Extensions.Net/HttpStatusCodeExtensions.cs index b297eb90f..81cff4f40 100644 --- a/src/Cuemon.Net/HttpStatusCodeExtensions.cs +++ b/src/Cuemon.Extensions.Net/HttpStatusCodeExtensions.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Cuemon.Net +namespace Cuemon.Extensions.Net { /// /// Extension methods for the enum. From da1404503fda6129046379f3b6439fb7a666660f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 01:59:35 +0200 Subject: [PATCH 028/385] Refactored to use VersionResult over Version. --- .../FileVersionInfoExtensions.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Cuemon.Extensions.Diagnostics/FileVersionInfoExtensions.cs b/src/Cuemon.Extensions.Diagnostics/FileVersionInfoExtensions.cs index 9fb12f80c..9b1eeba98 100644 --- a/src/Cuemon.Extensions.Diagnostics/FileVersionInfoExtensions.cs +++ b/src/Cuemon.Extensions.Diagnostics/FileVersionInfoExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using Cuemon.Reflection; namespace Cuemon.Extensions.Diagnostics { @@ -9,32 +10,30 @@ namespace Cuemon.Extensions.Diagnostics public static class FileVersionInfoExtensions { /// - /// Returns a from the specified . + /// Returns a from the specified . /// /// An instance of . - /// A that represents the product version that the is distributed with. - /// Should the specified not contain any product version, a initialized to 0.0.0.0 is returned. - public static Version ToProductVersion(this FileVersionInfo fvi) + /// A that represents the product version that the is distributed with. + public static VersionResult ToProductVersion(this FileVersionInfo fvi) { return ToVersion(fvi, info => info.ProductVersion); } /// - /// Returns a from the specified . + /// Returns a from the specified . /// /// An instance of . - /// A that represents the file version that the is distributed with. - /// Should the specified not contain any file version, a initialized to 0.0.0.0 is returned. - public static Version ToFileVersion(this FileVersionInfo fvi) + /// A that represents the file version that the is distributed with. + public static VersionResult ToFileVersion(this FileVersionInfo fvi) { return ToVersion(fvi, info => info.FileVersion); } - private static Version ToVersion(this FileVersionInfo fvi, Func propertySelector) + private static VersionResult ToVersion(this FileVersionInfo fvi, Func propertySelector) { Validator.ThrowIfNull(fvi, nameof(fvi)); var version = propertySelector(fvi); - return new Version(string.IsNullOrWhiteSpace(version) ? "0.0.0.0" : version); + return new VersionResult(version); } } } \ No newline at end of file From 53721114d5b9ecd60384a14a60028a8502c4cafb Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 02:00:35 +0200 Subject: [PATCH 029/385] Adjusted all project files to use Directory.Build.props as well as version.json to opt-in for semantic versioning of Cuemon. --- .../Cuemon.AspNetCore.Authentication.csproj | 19 +------------- .../Cuemon.AspNetCore.Mvc.csproj | 19 +------------- .../Cuemon.AspNetCore.Razor.csproj | 19 +------------- .../Cuemon.AspNetCore.csproj | 19 +------------- src/Cuemon.Core/Cuemon.Core.csproj | 18 ------------- .../Cuemon.Data.Integrity.csproj | 15 ++--------- .../Cuemon.Data.SqlClient.csproj | 13 +--------- src/Cuemon.Data/Cuemon.Data.csproj | 15 ++--------- .../Cuemon.Diagnostics.csproj | 13 +--------- ...Core.Mvc.Formatters.Newtonsoft.Json.csproj | 18 +------------ ...sions.AspNetCore.Mvc.Formatters.Xml.csproj | 19 +------------- .../Cuemon.Extensions.AspNetCore.Mvc.csproj | 19 +------------- .../Cuemon.Extensions.AspNetCore.csproj | 22 +--------------- ...emon.Extensions.Collections.Generic.csproj | 21 ++-------------- ....Extensions.Collections.Specialized.csproj | 21 ++-------------- .../Cuemon.Extensions.Core.csproj | 21 ++-------------- .../Cuemon.Extensions.Data.Integrity.csproj | 21 ++-------------- .../Cuemon.Extensions.Data.csproj | 14 ++--------- ...emon.Extensions.DependencyInjection.csproj | 21 ++-------------- .../Cuemon.Extensions.Diagnostics.csproj | 15 ++--------- .../Cuemon.Extensions.IO.csproj | 21 ++-------------- .../Cuemon.Extensions.Net.csproj | 25 ++----------------- .../Cuemon.Extensions.Newtonsoft.Json.csproj | 21 ++-------------- .../Cuemon.Extensions.Reflection.csproj | 20 ++------------- .../Cuemon.Extensions.Text.csproj | 21 ++-------------- .../Cuemon.Extensions.Threading.csproj | 21 ++-------------- .../Cuemon.Extensions.Xml.csproj | 20 ++------------- .../Cuemon.Extensions.Xunit.csproj | 21 ++-------------- src/Cuemon.IO/Cuemon.IO.csproj | 17 +++---------- src/Cuemon.Net/Cuemon.Net.csproj | 15 ++--------- .../Cuemon.Resilience.csproj | 15 ++--------- .../Cuemon.Runtime.Caching.csproj | 15 ++--------- src/Cuemon.Threading/Cuemon.Threading.csproj | 15 ++--------- src/Cuemon.Xml/Cuemon.Xml.csproj | 13 +--------- 34 files changed, 56 insertions(+), 566 deletions(-) diff --git a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj index 45690205d..d75a43e2b 100644 --- a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj +++ b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj @@ -2,31 +2,14 @@ netcoreapp3.0;netstandard2.0 - true - ..\cuemon.snk - true a10adf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.AspNetCore.Authentication Cuemon.AspNetCore.Authentication The Cuemon.AspNetCore.Authentication assembly provides supplemental ways of authentication forms to Microsoft ASP.NET Core. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + diff --git a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj index a882b1574..de8a5c9a7 100644 --- a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj +++ b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj @@ -2,31 +2,14 @@ netcoreapp3.0;netstandard2.0 - true - ..\cuemon.snk - true a20adf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.AspNetCore.Mvc Cuemon.AspNetCore.Mvc The Cuemon.AspNetCore.Mvc assembly is a fit companion to the Microsoft.AspNetCore.Mvc namespace that provides an abundant range of filters and action results to your codebelt. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + diff --git a/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj b/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj index 43b37b886..6161d5a53 100644 --- a/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj +++ b/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj @@ -2,31 +2,14 @@ netcoreapp3.0;netstandard2.0 - true - ..\cuemon.snk - true a30adf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.AspNetCore.Razor Cuemon.AspNetCore.Razor The Cuemon.AspNetCore.Razor assembly is a supplement to Microsoft ASP.NET Core. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + diff --git a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj index 4c98a08dc..2fa2d4eae 100644 --- a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj +++ b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj @@ -2,31 +2,14 @@ netcoreapp3.0;netstandard2.0 - true - ..\cuemon.snk - true a00adf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.AspNetCore Cuemon.AspNetCore The Cuemon.AspNetCore assembly is a supplement to Microsoft ASP.NET Core. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + diff --git a/src/Cuemon.Core/Cuemon.Core.csproj b/src/Cuemon.Core/Cuemon.Core.csproj index 6947dc04a..01301b8d6 100644 --- a/src/Cuemon.Core/Cuemon.Core.csproj +++ b/src/Cuemon.Core/Cuemon.Core.csproj @@ -2,32 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 000bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon Cuemon.Core Cuemon The Cuemon.Core assembly is the patriarch of the Cuemon family and provides fundamental-, utility- and base-classes that define commonly-used value and reference data types, events and event handlers, interfaces, attribute, and feature rich delegates to greatly support functional programming. A great addition to the well established System namespace to make your daily operations easier to work with. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT diff --git a/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj b/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj index 6f8c28e18..2a93f0b3d 100644 --- a/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj +++ b/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj @@ -2,25 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 130bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Data.Integrity Cuemon.Data.Integrity - The Cuemon.Data.Integrity assembly provides access to abstractions related to the System.Xml namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + The Cuemon.Data.Integrity assembly provides access to data integrity related operations. + cache-validator checksum data-integrity diff --git a/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj b/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj index 32dee29c1..841e11142 100644 --- a/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj +++ b/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj @@ -2,25 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 030bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Data.SqlClient Cuemon.Data.SqlClient The Cuemon.Data.SqlClient assembly provides different Microsoft SQL Server implementations of different abstractions found in the Cuemon.Data namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + sql sql-in-operator sql-query-builder sql-data-manager transient-fault-handling diff --git a/src/Cuemon.Data/Cuemon.Data.csproj b/src/Cuemon.Data/Cuemon.Data.csproj index 7b61dce3d..67ba7182d 100644 --- a/src/Cuemon.Data/Cuemon.Data.csproj +++ b/src/Cuemon.Data/Cuemon.Data.csproj @@ -1,26 +1,15 @@ - + netstandard2.0 - true - ..\cuemon.snk - true 110bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Data Cuemon.Data The Cuemon.Data assembly provides access to abstractions related to the System.Data namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + database db abstractions dto data-transfer row column bulk-copy diff --git a/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj b/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj index a69dfa3ab..0435a44c0 100644 --- a/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj +++ b/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj @@ -2,25 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 1a0bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Diagnostics Cuemon.Diagnostics The Cuemon.Diagnostics assembly provides access to features that extends the System.Diagnostics namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + time-measuring async-time-measuring profiler exception-descriptor diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj index 69a8a988c..564002462 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj @@ -2,9 +2,6 @@ netcoreapp3.0;netstandard2.0 - true - ..\cuemon.snk - true a60adf91-e7c7-4cb4-a39d-e1a5374c5602 @@ -13,20 +10,7 @@ Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json assembly provides extension methods and JSON formatters for ASP.NET Core MVC that uses the Newtonsoft.Json package. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + extension-methods extensions diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj index 7bb3ffed9..6f302dc69 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj @@ -2,31 +2,14 @@ netcoreapp3.0;netstandard2.0 - true - ..\cuemon.snk - true a70adf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml assembly provides extension methods and XML formatters for ASP.NET Core MVC. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + extension-methods extensions diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj index 0771f747b..0104c80fb 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj @@ -2,31 +2,14 @@ netcoreapp3.0;netstandard2.0 - true - ..\cuemon.snk - true a50adf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.Extensions.AspNetCore.Mvc Cuemon.Extensions.AspNetCore.Mvc The Cuemon.Extensions.AspNetCore.Mvc assembly provides extension methods and general improvement to the Cuemon.AspNetCore assembly. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + extension-methods extensions diff --git a/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj b/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj index 257dab972..542102cfa 100644 --- a/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj +++ b/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj @@ -2,31 +2,14 @@ netcoreapp3.0;netstandard2.0 - true - ..\cuemon.snk - true a40adf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.Extensions.AspNetCore Cuemon.Extensions.AspNetCore The Cuemon.Extensions.AspNetCore assembly provides extension methods and general improvement to the Cuemon.AspNetCore assembly. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + extension-methods extensions @@ -36,9 +19,6 @@ - - - \ No newline at end of file diff --git a/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj b/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj index 4c23d8675..1ec8efa93 100644 --- a/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj +++ b/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj @@ -2,31 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 190bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Extensions.Collections.Generic Cuemon.Extensions.Collections.Generic - The Cuemon.Extensions.Collections.Generic assembly provides extensions methods to the System.Collections.Generic namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.Collections.Generic assembly provides access to extension methods that supports the System.Collections.Generic namespace. + extension-methods extensions diff --git a/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj b/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj index 35701dc3c..6b8df096d 100644 --- a/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj +++ b/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj @@ -2,31 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 010bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.Extensions.Collections.Specialized Cuemon.Extensions.Collections.Specialized - The Cuemon.Extensions.Collections.Specialized assembly is a member of the Cuemon .NET Standard family and provides enhancements to the System.Collections.Specialized assembly. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.Collections.Specialized assembly provides access to extension methods that supports the Cuemon.Collections.Specialized namespace. + extension-methods extensions diff --git a/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj b/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj index 5c77a6457..8474d95a4 100644 --- a/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj +++ b/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj @@ -2,32 +2,15 @@ netstandard2.0 - true - ..\cuemon.snk - true 020bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.Extensions Cuemon.Extensions.Core Cuemon.Extensions - The Cuemon.Extensions.Core assembly is a member of the Cuemon .NET Standard family which provides extension methods to the Cuemon.Core assembly. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.Core assembly provides access to extension methods that supports the Cuemon namespace. + extension-methods extensions core diff --git a/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj b/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj index 36adf2a50..5aa829a1c 100644 --- a/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj +++ b/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj @@ -2,31 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 050bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Extensions.Data.Integrity Cuemon.Extensions.Data.Integrity - The Cuemon.Extensions.Data.Integrity assembly provides extension methods to the Cuemon.Data.Integrity namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.Data.Integrity assembly provides access to extension methods that supports the Cuemon.Data.Integrity namespace. + extension-methods extensions get-cache-validator combine-with diff --git a/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj b/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj index 28b73bfbb..b5f9258c6 100644 --- a/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj +++ b/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj @@ -2,9 +2,6 @@ netstandard2.0 - true - ..\cuemon.snk - true 100bdf91-e7c7-4cb4-a39d-e1a5374c5602 @@ -12,15 +9,8 @@ Cuemon Cuemon.Extensions.Data Cuemon.Extensions.Data - The Cuemon.Extensions.Data assembly provides extension methods related to the Cuemon.Data namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + The Cuemon.Extensions.Data assembly provides access to extension methods that supports the Cuemon.Data namespace. + extension-methods extensions to-rows to-columns embed diff --git a/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj b/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj index 58275a69b..48ab3709c 100644 --- a/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj +++ b/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj @@ -2,31 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 040bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.Extensions.DependencyInjection Cuemon.Extensions.DependencyInjection - The Cuemon.Extensions.DependencyInjection assembly provides extension methods to the Microsoft.Extensions.DependencyInjection package. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://opensource.org/licenses/MIT - https://www.cuemon.net/ - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png + The Cuemon.Extensions.DependencyInjection assembly provides access to extension methods that supports the Microsoft.Extensions.DependencyInjection namespace. + extension-methods extensions add tryadd diff --git a/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj b/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj index 91d4b068a..d8bccdad7 100644 --- a/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj +++ b/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj @@ -2,25 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 0f0bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Extensions.Diagnostics Cuemon.Extensions.Diagnostics - The Cuemon.Extensions.Diagnostics assembly provides extension methods for diagnostics related implementations. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + The Cuemon.Extensions.Diagnostics assembly provides access to extension methods that supports the Cuemon.Diagnostics namespace. + extension-methods extensions to-insights-string to-product-version to-file-version diff --git a/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj b/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj index 58e53ad13..8294119bd 100644 --- a/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj +++ b/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj @@ -2,31 +2,14 @@ netstandard2.0;netstandard2.1 - true - ..\cuemon.snk - true 060bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.Extensions.IO Cuemon.Extensions.IO - The Cuemon.Extensions.IO assembly provides extension methods to the System.IO namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.IO assembly provides access to extension methods that supports the System.IO namespace. + extension-methods extensions to-byte-array to-byte-array-async write-async to-encoded-string to-encoded-string-async compress-brotli compress-brotli-async compress-deflate compress-deflate-async compress-gzip compress-gzip-async diff --git a/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj b/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj index fe5915404..3b551bb09 100644 --- a/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj +++ b/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj @@ -2,41 +2,20 @@ netstandard2.0 - true - ..\cuemon.snk - true 070bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Extensions.Net Cuemon.Extensions.Net - The Cuemon.Extensions.Net assembly provides extensions methods (query-string parsing, encoding, decoding, security and http communication) and features related to the System.Net namespace. A versatile HttpManager that transparently promotes the HttpClient is included along with a lightweight SMTP Client. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.Net assembly provides extension methods (query-string parsing, encoding, decoding, security and http communication) and features related to the System.Net namespace. A versatile HttpManager that transparently promotes the HttpClient is included. + extension-methods extensions to-signed-uri validate-signed-uri http-manager-factory slim-http-client-factory i-http-client-factory - - - - diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj index f75c0622f..9d289ec9d 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj @@ -2,31 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 080bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.Extensions.Newtonsoft.Json Cuemon.Extensions.Newtonsoft.Json - The Cuemon.Extensions.Newtonsoft.Json assembly provides extension methods and general improvements to the Newtonsoft.Json package. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.Newtonsoft.Json assembly provides extension methods and general improvements that supports the Newtonsoft.Json namespace. + extension-methods extensions jdata jdata-result json-converter json-formatter diff --git a/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj b/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj index c9878269c..cd4bba0db 100644 --- a/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj +++ b/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj @@ -2,9 +2,6 @@ netstandard2.0 - true - ..\cuemon.snk - true 090bdf91-e7c7-4cb4-a39d-e1a5374c5602 @@ -12,21 +9,8 @@ Cuemon .NET Standard Cuemon.Extensions.Reflection Cuemon.Extensions.Reflection - The Cuemon.Extensions.Reflection assembly provides extension to the System.Reflection namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.Reflection assembly provides extension methods to the System.Reflection namespace. + extension-methods extensions get-assembly-version get-file-version get-product-version is-debug-build has-attributes is-auto-property get-runtime-properties-except-of diff --git a/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj b/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj index 1ee5e3754..d3600fddc 100644 --- a/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj +++ b/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj @@ -2,31 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 0a0bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.Extensions.Text Cuemon.Extensions.Text - The Cuemon.Extensions.Text assembly provides extension methods to the Cuemon.Text namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.Text assembly provides access to extension methods for the Cuemon.Text namespace. + extension-methods extensions to-encoded-string to-ascii-encoded-string diff --git a/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj b/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj index c0dc2ac85..d23ead4ad 100644 --- a/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj +++ b/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj @@ -2,31 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 180bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Extensions.Threading Cuemon.Extensions.Threading - The Cuemon.Extensions.Threading assembly provides enhancements to the System.Threading namespace as well as extensions methods for XML scenarios such as escaping, conversions, parsing, serialization and deserialization. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.Threading assembly provides access to extension methods for the System.Threading namespace. + extension-methods extensions continue-with-captured-context continue-with-suppressed-context diff --git a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj index bea42dad1..36de71b95 100644 --- a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj +++ b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj @@ -2,9 +2,6 @@ netstandard2.0 - true - ..\cuemon.snk - true 0c0bdf91-e7c7-4cb4-a39d-e1a5374c5602 @@ -12,21 +9,8 @@ Cuemon Cuemon.Extensions.Xml Cuemon.Extensions.Xml - The Cuemon.Extensions.Xml assembly provides several extension methods for XML scenarios such as escaping, conversions, parsing, sanitizing, serialization and deserialization. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.Xml assembly provides access to extension methods for XML scenarios such as escaping, conversions, parsing, sanitizing, serialization and deserialization. + extension-methods extensions diff --git a/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj b/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj index 4edfee77e..5e76b7c76 100644 --- a/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj +++ b/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj @@ -2,31 +2,14 @@ netstandard2.0;netcoreapp3.0 - true - ..\cuemon.snk - true 0d0bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon .NET Standard Cuemon.Extensions.Xunit Cuemon.Extensions.Xunit - The Cuemon.Extensions.Xunit assembly provides extension methods and general improvements to the xunit.abstractions package. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US - - - - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png - https://www.cuemon.net/ - https://opensource.org/licenses/MIT + The Cuemon.Extensions.Xunit assembly provides access to extension methods and abstractions that supports the Xunit.Abstractions namespace. + test host-test diff --git a/src/Cuemon.IO/Cuemon.IO.csproj b/src/Cuemon.IO/Cuemon.IO.csproj index e01f5a38e..ea8936de7 100644 --- a/src/Cuemon.IO/Cuemon.IO.csproj +++ b/src/Cuemon.IO/Cuemon.IO.csproj @@ -1,26 +1,15 @@ - + netstandard2.0;netstandard2.1 - true - ..\cuemon.snk - true 170bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.IO Cuemon.IO - The Cuemon.IO assembly provides extensions methods and a lightweight resilience framework to support transient fault handling. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + The Cuemon.IO assembly provides access to features that extends the System.IO namespace through IDecorator extension methods. + textreader textwriter brotli gzip deflate async diff --git a/src/Cuemon.Net/Cuemon.Net.csproj b/src/Cuemon.Net/Cuemon.Net.csproj index b6cbc9554..871d90de9 100644 --- a/src/Cuemon.Net/Cuemon.Net.csproj +++ b/src/Cuemon.Net/Cuemon.Net.csproj @@ -2,25 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 140bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Net Cuemon.Net - The Cuemon.Net assembly provides access to abstractions related to the System.Xml namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + The Cuemon.Net assembly provides access to features that extends the System.Net namespace and includes a lightweight SMTP Client. + http-manager http-get http-post http-put http-patch http-delete http-trace mail-distributor smtp-client diff --git a/src/Cuemon.Resilience/Cuemon.Resilience.csproj b/src/Cuemon.Resilience/Cuemon.Resilience.csproj index 6e1490824..792681e01 100644 --- a/src/Cuemon.Resilience/Cuemon.Resilience.csproj +++ b/src/Cuemon.Resilience/Cuemon.Resilience.csproj @@ -2,25 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 0e0bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Resilience Cuemon.Resilience - The Cuemon.Resilience assembly provides extension methods and a lightweight resilience framework to support transient fault handling. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + The Cuemon.Resilience assembly provides access to a lightweight resilience framework that support transient fault handling of operations. + transient-fault-evidence transient-fault-exception transient-operation async-transient-operation latency-exception diff --git a/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj b/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj index d70b38eed..8dfc44eb1 100644 --- a/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj +++ b/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj @@ -2,25 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 160bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Runtime.Caching Cuemon.Runtime.Caching - The Cuemon.Runtime.Caching assembly provides access to abstractions related to the System.Threading namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + The Cuemon.Runtime.Caching assembly provides access to features that extends the System.Runtime.Caching namespace. + caching-manager diff --git a/src/Cuemon.Threading/Cuemon.Threading.csproj b/src/Cuemon.Threading/Cuemon.Threading.csproj index 7a5ab6279..f60bccbd7 100644 --- a/src/Cuemon.Threading/Cuemon.Threading.csproj +++ b/src/Cuemon.Threading/Cuemon.Threading.csproj @@ -2,25 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 150bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Threading Cuemon.Threading - The Cuemon.Threading assembly provides access to abstractions related to the System.Threading namespace. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + The Cuemon.Threading assembly provides access to features that extends the System.Threading namespace. + parallel-factory for-async for-each-async for-each-result-async for-result-async while-async while-result-async diff --git a/src/Cuemon.Xml/Cuemon.Xml.csproj b/src/Cuemon.Xml/Cuemon.Xml.csproj index a906e3618..4a31fc077 100644 --- a/src/Cuemon.Xml/Cuemon.Xml.csproj +++ b/src/Cuemon.Xml/Cuemon.Xml.csproj @@ -2,25 +2,14 @@ netstandard2.0 - true - ..\cuemon.snk - true 120bdf91-e7c7-4cb4-a39d-e1a5374c5602 - Cuemon Cuemon.Xml Cuemon.Xml The Cuemon.Xml assembly provides access to features that extends both the System.Xml- and System.Xml.Serialization namespaces. Included is a lightweight XML serializer framework that offers the same flexibility provided by the JSON equivalent from Newtonsoft. - Geekle - Michael Mortensen - Copyright © Geekle 2009-2020. All rights reserved. - 6.0.2020.25 - 6.0.2020.25 - 6.0.2020.0 - Michael Mortensen - en-US + xml-formatter xml-converter xml-serializer xml-factories From 30bab3cd10fab5a90a7d2d6195ee7231f34a72bb Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 14:57:42 +0200 Subject: [PATCH 030/385] Added SonarLint to integrate with SonarQube. --- Directory.Build.props | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Directory.Build.props b/Directory.Build.props index 53f7966ef..e815ca5f2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -20,8 +20,18 @@ true ..\cuemon.snk true + ..\..\.sonarlint\cuemoncorecsharp.ruleset + + + + + + + + + From 9f6683bc0419b118ae20c90e8fb7ba2df6395d1d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 19:32:34 +0200 Subject: [PATCH 031/385] Justified S1751 --- src/Cuemon.Core/GlobalSuppressions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Cuemon.Core/GlobalSuppressions.cs b/src/Cuemon.Core/GlobalSuppressions.cs index 7554af4d8..f575988b4 100644 --- a/src/Cuemon.Core/GlobalSuppressions.cs +++ b/src/Cuemon.Core/GlobalSuppressions.cs @@ -46,10 +46,11 @@ [assembly: SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Validator.ThrowIfUri(System.String,System.UriKind,System.String,System.String)")] [assembly: SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Validator.ThrowIfWhiteSpace(System.String,System.String,System.String)")] [assembly: SuppressMessage("Major Code Smell", "S3881:\"IDisposable\" should be implemented correctly", Justification = "This is a base class implementation of the IDisposable interface tailored to avoid wrong implementations.", Scope = "type", Target = "~T:Cuemon.Disposable")] -[assembly: SuppressMessage("Major Code Smell", "S2589:Boolean expressions should not be gratuitous", Justification = "Bug in Sonar?", Scope = "member", Target = "~M:Cuemon.ExceptionInsights.ToExceptionDescriptor(System.Exception,System.String,System.String,System.Uri)~Cuemon.Diagnostics.ExceptionDescriptor")] [assembly: SuppressMessage("Minor Code Smell", "S1128:Unused \"using\" should be removed", Justification = "It is actually used when resolving extension method from System.Collections.Generic; SC just can't figure this out.")] [assembly: SuppressMessage("Major Code Smell", "S1168:Empty arrays and collections should be returned instead of null", Justification = "By design; property serves it purpose.", Scope = "member", Target = "~P:Cuemon.Data.ConcurrentDsvDataReader.NullRead")] [assembly: SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "By design to help clarify context.", Scope = "member", Target = "~M:Cuemon.Data.ConcurrentDsvDataReader.ReadNext(System.String[])~System.String[]")] [assembly: SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "By design to help clarify context.", Scope = "member", Target = "~M:Cuemon.Data.DsvDataReader.ReadNext(System.String[])~System.String[]")] [assembly: SuppressMessage("Major Code Smell", "S1168:Empty arrays and collections should be returned instead of null", Justification = "By design; property serves it purpose.", Scope = "member", Target = "~P:Cuemon.Data.DsvDataReader.NullRead")] [assembly: SuppressMessage("Minor Code Smell", "S1199:Nested code blocks should not be used", Justification = "By design.", Scope = "member", Target = "~M:Cuemon.IO.StreamFactory.CreateStreamCore``1(Cuemon.ActionFactory{``0},System.Action{Cuemon.IO.StreamWriterOptions})~System.IO.Stream")] +[assembly: SuppressMessage("Major Bug", "S1751:Loops with at most one iteration should be refactored", Justification = "This is by design and how a reader is implemented. While there are lines to be read, we built a token, and the token is being read. When read, it returns true and proceeds to next line.", Scope = "member", Target = "~M:Cuemon.Data.DsvDataReader.Read~System.Boolean")] +[assembly: SuppressMessage("Major Bug", "S1751:Loops with at most one iteration should be refactored", Justification = "This is by design and how a reader is implemented. While there are lines to be read, we built a token, and the token is being read. When read, it returns true and proceeds to next line.", Scope = "member", Target = "~M:Cuemon.Data.ConcurrentDsvDataReader.ReadAsync~System.Threading.Tasks.Task{System.Boolean}")] From df7b98dcf52d12b07232970555c424e516f0a7b5 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 19:33:10 +0200 Subject: [PATCH 032/385] Added a few MVP tests. --- .../Assets/DsvDataReaderTest_Wiki.csv | 6 +++ .../Cuemon.Core.Tests.csproj | 8 ++++ .../Data/ConcurrentDsvDataReaderTest.cs | 36 ++++++++++++++++++ .../Data/DsvDataReaderTest.cs | 35 +++++++++++++++++ test/Cuemon.Core.Tests/GlobalSuppressions.cs | 10 ----- .../Security/Cryptography/AesCryptorTest.cs | 38 +++++++++++++++++++ 6 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 test/Cuemon.Core.Tests/Assets/DsvDataReaderTest_Wiki.csv create mode 100644 test/Cuemon.Core.Tests/Data/ConcurrentDsvDataReaderTest.cs create mode 100644 test/Cuemon.Core.Tests/Data/DsvDataReaderTest.cs delete mode 100644 test/Cuemon.Core.Tests/GlobalSuppressions.cs create mode 100644 test/Cuemon.Core.Tests/Security/Cryptography/AesCryptorTest.cs diff --git a/test/Cuemon.Core.Tests/Assets/DsvDataReaderTest_Wiki.csv b/test/Cuemon.Core.Tests/Assets/DsvDataReaderTest_Wiki.csv new file mode 100644 index 000000000..6d87d0f5d --- /dev/null +++ b/test/Cuemon.Core.Tests/Assets/DsvDataReaderTest_Wiki.csv @@ -0,0 +1,6 @@ +Year,Make,Model,Description,Price +1997,Ford,E350,"ac, abs, moon",3000.00 +1999,Chevy,"Venture ""Extended Edition""","",4900.00 +1999,Chevy,"Venture ""Extended Edition, Very Large""",,5000.00 +1996,Jeep,Grand Cherokee,"MUST SELL! +air, moon roof, loaded",4799.50 \ No newline at end of file diff --git a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj index 838fce310..9a2336890 100644 --- a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj +++ b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj @@ -4,6 +4,14 @@ Cuemon + + + + + + + + diff --git a/test/Cuemon.Core.Tests/Data/ConcurrentDsvDataReaderTest.cs b/test/Cuemon.Core.Tests/Data/ConcurrentDsvDataReaderTest.cs new file mode 100644 index 000000000..b44ae47f0 --- /dev/null +++ b/test/Cuemon.Core.Tests/Data/ConcurrentDsvDataReaderTest.cs @@ -0,0 +1,36 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Cuemon.Reflection; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Data +{ + public class ConcurrentDsvDataReaderTest : Test + { + public ConcurrentDsvDataReaderTest(ITestOutputHelper output = null) : base(output) + { + } + + [Fact] + public async Task DsvDataReader_ShouldReadEmbeddedResourceLineByLine() + { + var file = Decorator.Enclose(typeof(DsvDataReaderTest).Assembly).GetManifestResources("DsvDataReaderTest_Wiki.csv", ManifestResourceMatch.ContainsName).Values.Single(); + ConcurrentDsvDataReader reader = null; + using (reader = new ConcurrentDsvDataReader(new StreamReader(file))) + { + while (await reader.ReadAsync()) + { + TestOutput.WriteLine(reader.ToString()); + } + } + Assert.Equal(5, reader.FieldCount); + Assert.Equal(4, reader.RowCount); + Assert.True(reader.Disposed); + await Assert.ThrowsAsync(() => reader.ReadAsync()); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Core.Tests/Data/DsvDataReaderTest.cs b/test/Cuemon.Core.Tests/Data/DsvDataReaderTest.cs new file mode 100644 index 000000000..eab09eda7 --- /dev/null +++ b/test/Cuemon.Core.Tests/Data/DsvDataReaderTest.cs @@ -0,0 +1,35 @@ +using System; +using System.IO; +using System.Linq; +using Cuemon.Extensions.Xunit; +using Cuemon.Reflection; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Data +{ + public class DsvDataReaderTest : Test + { + public DsvDataReaderTest(ITestOutputHelper output = null) : base(output) + { + } + + [Fact] + public void DsvDataReader_ShouldReadEmbeddedResourceLineByLine() + { + var file = Decorator.Enclose(typeof(DsvDataReaderTest).Assembly).GetManifestResources("DsvDataReaderTest_Wiki.csv", ManifestResourceMatch.ContainsName).Values.Single(); + DsvDataReader reader = null; + using (reader = new DsvDataReader(new StreamReader(file))) + { + while (reader.Read()) + { + TestOutput.WriteLine(reader.ToString()); + } + } + Assert.Equal(5, reader.FieldCount); + Assert.Equal(4, reader.RowCount); + Assert.True(reader.Disposed); + Assert.Throws(() => reader.Read()); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Core.Tests/GlobalSuppressions.cs b/test/Cuemon.Core.Tests/GlobalSuppressions.cs deleted file mode 100644 index 4fbc51c9c..000000000 --- a/test/Cuemon.Core.Tests/GlobalSuppressions.cs +++ /dev/null @@ -1,10 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.DisposableTest.SafeInvokeAsync_ShouldAbideRuleCA2000~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Minor Code Smell", "S3604:Member initializer values should not be redundant", Justification = "", Scope = "member", Target = "~F:Cuemon.Assets.UnmanagedDisposable._handle")] -[assembly: SuppressMessage("Critical Code Smell", "S1215:\"GC.Collect\" should not be called", Justification = "", Scope = "member", Target = "~M:Cuemon.DisposableTest.UnmanagedDisposable_VerifyThatAssetIsBeingDisposedOnFinalize")] diff --git a/test/Cuemon.Core.Tests/Security/Cryptography/AesCryptorTest.cs b/test/Cuemon.Core.Tests/Security/Cryptography/AesCryptorTest.cs new file mode 100644 index 000000000..2c576aebd --- /dev/null +++ b/test/Cuemon.Core.Tests/Security/Cryptography/AesCryptorTest.cs @@ -0,0 +1,38 @@ +using System; +using System.Linq; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Security.Cryptography +{ + public class AesCryptorTest : Test + { + private readonly byte[] _secretKey; + private readonly byte[] _iv; + + public AesCryptorTest(ITestOutputHelper output = null) : base(output) + { + _secretKey = AesCryptor.GenerateKey(); + _iv = AesCryptor.GenerateInitializationVector(); + } + + [Fact] + public void AesCryptor_ShouldEncryptAndDecrypt() + { + var cryptor = new AesCryptor(_secretKey, _iv); + var secretMessage = Decorator.Enclose("This is my secret message that needs encryption!").ToByteArray(); + + Assert.True(_secretKey.SequenceEqual(cryptor.Key)); + Assert.True(_iv.SequenceEqual(cryptor.InitializationVector)); + + var enc = cryptor.Encrypt(secretMessage); + TestOutput.WriteLine(Convert.ToBase64String(enc)); + + var dec = cryptor.Decrypt(enc); + TestOutput.WriteLine(Convert.ToBase64String(dec)); + + Assert.True(dec.SequenceEqual(secretMessage)); + } + } +} \ No newline at end of file From ef26b48cfad1fcedd3a91d957740540822ed7fc6 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 19:33:45 +0200 Subject: [PATCH 033/385] For consistency - moved ER to Assets. --- test/Cuemon.Extensions.Xml.Tests/{ => Assets}/Namespace.xml | 0 .../Cuemon.Extensions.Xml.Tests.csproj | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename test/Cuemon.Extensions.Xml.Tests/{ => Assets}/Namespace.xml (100%) diff --git a/test/Cuemon.Extensions.Xml.Tests/Namespace.xml b/test/Cuemon.Extensions.Xml.Tests/Assets/Namespace.xml similarity index 100% rename from test/Cuemon.Extensions.Xml.Tests/Namespace.xml rename to test/Cuemon.Extensions.Xml.Tests/Assets/Namespace.xml diff --git a/test/Cuemon.Extensions.Xml.Tests/Cuemon.Extensions.Xml.Tests.csproj b/test/Cuemon.Extensions.Xml.Tests/Cuemon.Extensions.Xml.Tests.csproj index d48d6f5ec..d0573d643 100644 --- a/test/Cuemon.Extensions.Xml.Tests/Cuemon.Extensions.Xml.Tests.csproj +++ b/test/Cuemon.Extensions.Xml.Tests/Cuemon.Extensions.Xml.Tests.csproj @@ -9,7 +9,7 @@ - + From eb0511aaa1b4609e78fff0a7006989a6bb900761 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 19:34:11 +0200 Subject: [PATCH 034/385] Disabled analyzers on test projects. --- Directory.Build.props | 1 + .../GlobalSuppressions.cs | 29 ------------------- test/Cuemon.IO.Tests/GlobalSuppressions.cs | 12 -------- .../GlobalSuppressions.cs | 16 ---------- 4 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 test/Cuemon.Diagnostics.Tests/GlobalSuppressions.cs delete mode 100644 test/Cuemon.IO.Tests/GlobalSuppressions.cs delete mode 100644 test/Cuemon.Resilience.Tests/GlobalSuppressions.cs diff --git a/Directory.Build.props b/Directory.Build.props index e815ca5f2..41c01556c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -40,6 +40,7 @@ netcoreapp3.0 false + false diff --git a/test/Cuemon.Diagnostics.Tests/GlobalSuppressions.cs b/test/Cuemon.Diagnostics.Tests/GlobalSuppressions.cs deleted file mode 100644 index 53ac92000..000000000 --- a/test/Cuemon.Diagnostics.Tests/GlobalSuppressions.cs +++ /dev/null @@ -1,29 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithActionAsync_Use_0_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithActionAsync_Use_1_Argument_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithActionAsync_Use_10_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithActionAsync_Use_2_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithActionAsync_Use_3_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithActionAsync_Use_4_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithActionAsync_Use_5_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithActionAsync_Use_6_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithActionAsync_Use_7_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithActionAsync_Use_8_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithActionAsync_Use_9_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithFuncAsync_Use_0_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithFuncAsync_Use_1_Argument_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithFuncAsync_Use_10_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithFuncAsync_Use_2_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithFuncAsync_Use_3_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithFuncAsync_Use_4_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithFuncAsync_Use_5_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithFuncAsync_Use_6_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithFuncAsync_Use_7_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithFuncAsync_Use_8_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasureTest.WithFuncAsync_Use_9_Arguments_And_CancellationToken_ShouldTakeAroundOneSecond~System.Threading.Tasks.Task")] diff --git a/test/Cuemon.IO.Tests/GlobalSuppressions.cs b/test/Cuemon.IO.Tests/GlobalSuppressions.cs deleted file mode 100644 index c42e28595..000000000 --- a/test/Cuemon.IO.Tests/GlobalSuppressions.cs +++ /dev/null @@ -1,12 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.IO.StreamDecoratorExtensionsTest.CompressBrotliAsync_ShouldCompressAndDecompress~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.IO.StreamDecoratorExtensionsTest.CompressGZipAsync_ShouldCompressAndDecompress~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.IO.StreamDecoratorExtensionsTest.CompressGZipAsync_ShouldThrowTaskCanceledException~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.IO.StreamDecoratorExtensionsTest.ToByteArrayAsync_ShouldConvertStreamToByteArrayWithDefaultOptions~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.IO.StreamDecoratorExtensionsTest.ToEncodedStringAsync_ShouldConvertStreamToString~System.Threading.Tasks.Task")] diff --git a/test/Cuemon.Resilience.Tests/GlobalSuppressions.cs b/test/Cuemon.Resilience.Tests/GlobalSuppressions.cs deleted file mode 100644 index d8d0612f9..000000000 --- a/test/Cuemon.Resilience.Tests/GlobalSuppressions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperationTest.WithActionAsync_ShouldTriggerInvalidOperationException~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperationTest.WithActionAsync_ShouldTriggerLatencyException~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperationTest.WithActionAsync_ShouldTriggerTransientFaultException~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperationTest.WithActionAsync_ShouldBypassTransientFaultHandling~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperationTest.WithActionAsync_ShouldTriggerRetryAndSucceed~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperationTest.WithFuncAsync_ShouldTriggerInvalidOperationException~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperationTest.WithFuncAsync_ShouldTriggerLatencyException~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperationTest.WithFuncAsync_ShouldTriggerRetryAndSucceedAsync~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("ConfigureAwait", "ConfigureAwaitEnforcer:ConfigureAwaitEnforcer", Justification = "", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperationTest.WithFuncAsync_ShouldTriggerTransientFaultException~System.Threading.Tasks.Task")] From c98a9aed52635efd71c2f9b768a322ae7ed3d4aa Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 19:53:51 +0200 Subject: [PATCH 035/385] Fixes S1751. Note to self: Paged* should be rewritten. Legacy code. --- .../Collections/Generic/PagedSettings.cs | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/Cuemon.Core/Collections/Generic/PagedSettings.cs b/src/Cuemon.Core/Collections/Generic/PagedSettings.cs index 30aad9b53..1d43cb114 100644 --- a/src/Cuemon.Core/Collections/Generic/PagedSettings.cs +++ b/src/Cuemon.Core/Collections/Generic/PagedSettings.cs @@ -1,21 +1,17 @@ -namespace Cuemon.Collections.Generic +using System; + +namespace Cuemon.Collections.Generic { /// - /// Specifies a set of features to support on the object. This class cannot be inherited. + /// Specifies a set of features to support on the object. /// - public sealed class PagedSettings + public class PagedSettings : IEquatable { - private static int DefaultPageSizeValue = 25; - /// /// Gets or sets the default page size of the class. Default is 25. /// /// The default page size of the class. - public static int DefaultPageSize - { - get { return DefaultPageSizeValue; } - set { DefaultPageSizeValue = value; } - } + public static int DefaultPageSize { get; set; } = 25; #region Constructors /// @@ -41,6 +37,31 @@ public override int GetHashCode() return Generate.HashCode32(PageSize, PageNumber, (int)SortOrderDirection, Data.GetHashCode()) ^ Generate.HashCode32(string.Concat(SearchCriteria, SortOrderBy)); } + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// true if the current object is equal to the other parameter; otherwise, false. + public virtual bool Equals(PagedSettings other) + { + if (ReferenceEquals(null, other)) { return false; } + if (ReferenceEquals(this, other)) { return true; } + return PageSize == other.PageSize && PageNumber.Equals(other.PageNumber) && SortOrderDirection.Equals(other.SortOrderDirection) && Data.GetHashCode().Equals(other.Data.GetHashCode()) && SearchCriteria.Equals(other.SearchCriteria) && SortOrderBy.Equals(other.SortOrderBy); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The object to compare with the current object. + /// true if the specified is equal to this instance; otherwise, false. + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { return false; } + if (ReferenceEquals(this, obj)) { return true; } + if (obj.GetType() != this.GetType()) { return false; } + return Equals((PagedSettings) obj); + } + #endregion #region Properties From fce97db7d8e9d99eb34cb3dd80aba3b8040d6762 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 27 Aug 2020 19:54:02 +0200 Subject: [PATCH 036/385] Tweaking versioning. --- version.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.json b/version.json index 75a57abf6..8b3d9a59c 100644 --- a/version.json +++ b/version.json @@ -7,7 +7,8 @@ }, "publicReleaseRefSpec": [ "^refs/heads/master$", - "^refs/heads/v\\d+(?:\\.\\d+)?$" + "^refs/heads/release$", + "^refs/heads/development$" ], "nugetPackageVersion": { "semVer": 2 From ceeaa06372b893e0af36658c9e95fb3df9bd031c Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 27 Aug 2020 22:30:57 +0200 Subject: [PATCH 037/385] Set up CI with Azure Pipelines Migration from current classic pipe to yaml [skip ci] --- azure-pipelines.yml | 89 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000..bdca3ad78 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,89 @@ +trigger: +- development + +pool: + vmImage: 'ubuntu-latest' + +steps: +- task: UseDotNet@2 + displayName: 'Use .Net Core SDK 2.2.110 (SonarCloud)' + inputs: + version: 2.2.110 + +- task: UseDotNet@2 + displayName: 'Use .Net Core 3.1.302' + inputs: + version: 3.1.302 + +- task: DotNetCoreCLI@2 + displayName: 'Install NBGV tool' + inputs: + command: custom + custom: tool + arguments: 'install --global nbgv' + +- script: 'nbgv cloud' + displayName: 'Set Version using NBGV' + +- task: DotNetCoreCLI@2 + displayName: Restore + inputs: + command: restore + projects: '$(Parameters.projects)' + +- task: DownloadSecureFile@1 + displayName: 'Download cuemon.snk' + inputs: + secureFile: '15606b59-2e5f-4cf8-a50d-66fa026bb391' + +- task: CopyFiles@2 + displayName: 'Copy cuemon.snk to $(System.DefaultWorkingDirectory)\$(BuildSource)' + inputs: + SourceFolder: '$(Agent.TempDirectory)' + Contents: cuemon.snk + TargetFolder: '$(System.DefaultWorkingDirectory)\$(BuildSource)' + +# INSERT SONAR CLOUD PREPARE HERE + +- task: DotNetCoreCLI@2 + displayName: 'Build netcoreapp3.0' + inputs: + projects: | + src/**/Cuemon.AspNetCore*.csproj + src/**/Cuemon.Extensions.AspNetCore*.csproj + src/**/Cuemon.Extensions.Xunit.csproj + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netcoreapp3.0 --framework netcoreapp3.0' + workingDirectory: '$(BuildSource)' + +- task: DotNetCoreCLI@2 + displayName: 'Build netstandard2.1' + inputs: + projects: | + src/**/Cuemon.Extensions.IO.csproj + src/**/Cuemon.IO.csproj + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.1 --framework netstandard2.1' + workingDirectory: '$(BuildSource)' + +- task: DotNetCoreCLI@2 + displayName: 'Build netstandard2.0' + inputs: + projects: 'src/**/*.csproj' + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.0 --framework netstandard2.0' + workingDirectory: '$(BuildSource)' + +- task: DotNetCoreCLI@2 + displayName: Test + inputs: + command: test + projects: 'test/**/*.csproj' + arguments: '--configuration $(BuildConfiguration) --collect "Code coverage" ' + +# INSERT SONAR CLOUD RUN ANALYSIS + + +# INSERT SONAR CLOUD PUBLISH QUALITY GATE RESULT + +- task: PublishBuildArtifacts@1 + displayName: 'Publish Artifact: Cuemon' + inputs: + ArtifactName: Cuemon \ No newline at end of file From 7c4e3d725a50da620ec0d21428d3d9c9cfa2a02e Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 27 Aug 2020 22:43:07 +0200 Subject: [PATCH 038/385] Set up CI with Azure Pipelines Migration from current classic pipe to yaml [skip ci] --- azure-pipelines-1.yml | 89 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 azure-pipelines-1.yml diff --git a/azure-pipelines-1.yml b/azure-pipelines-1.yml new file mode 100644 index 000000000..bdca3ad78 --- /dev/null +++ b/azure-pipelines-1.yml @@ -0,0 +1,89 @@ +trigger: +- development + +pool: + vmImage: 'ubuntu-latest' + +steps: +- task: UseDotNet@2 + displayName: 'Use .Net Core SDK 2.2.110 (SonarCloud)' + inputs: + version: 2.2.110 + +- task: UseDotNet@2 + displayName: 'Use .Net Core 3.1.302' + inputs: + version: 3.1.302 + +- task: DotNetCoreCLI@2 + displayName: 'Install NBGV tool' + inputs: + command: custom + custom: tool + arguments: 'install --global nbgv' + +- script: 'nbgv cloud' + displayName: 'Set Version using NBGV' + +- task: DotNetCoreCLI@2 + displayName: Restore + inputs: + command: restore + projects: '$(Parameters.projects)' + +- task: DownloadSecureFile@1 + displayName: 'Download cuemon.snk' + inputs: + secureFile: '15606b59-2e5f-4cf8-a50d-66fa026bb391' + +- task: CopyFiles@2 + displayName: 'Copy cuemon.snk to $(System.DefaultWorkingDirectory)\$(BuildSource)' + inputs: + SourceFolder: '$(Agent.TempDirectory)' + Contents: cuemon.snk + TargetFolder: '$(System.DefaultWorkingDirectory)\$(BuildSource)' + +# INSERT SONAR CLOUD PREPARE HERE + +- task: DotNetCoreCLI@2 + displayName: 'Build netcoreapp3.0' + inputs: + projects: | + src/**/Cuemon.AspNetCore*.csproj + src/**/Cuemon.Extensions.AspNetCore*.csproj + src/**/Cuemon.Extensions.Xunit.csproj + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netcoreapp3.0 --framework netcoreapp3.0' + workingDirectory: '$(BuildSource)' + +- task: DotNetCoreCLI@2 + displayName: 'Build netstandard2.1' + inputs: + projects: | + src/**/Cuemon.Extensions.IO.csproj + src/**/Cuemon.IO.csproj + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.1 --framework netstandard2.1' + workingDirectory: '$(BuildSource)' + +- task: DotNetCoreCLI@2 + displayName: 'Build netstandard2.0' + inputs: + projects: 'src/**/*.csproj' + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.0 --framework netstandard2.0' + workingDirectory: '$(BuildSource)' + +- task: DotNetCoreCLI@2 + displayName: Test + inputs: + command: test + projects: 'test/**/*.csproj' + arguments: '--configuration $(BuildConfiguration) --collect "Code coverage" ' + +# INSERT SONAR CLOUD RUN ANALYSIS + + +# INSERT SONAR CLOUD PUBLISH QUALITY GATE RESULT + +- task: PublishBuildArtifacts@1 + displayName: 'Publish Artifact: Cuemon' + inputs: + ArtifactName: Cuemon \ No newline at end of file From 5aeaec81fbc39fd7ae9c1690306b823b0261a18e Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 27 Aug 2020 23:15:59 +0200 Subject: [PATCH 039/385] Set up CI with Azure Pipelines [skip ci] --- azure-pipelines.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bdca3ad78..34734b89d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -43,7 +43,16 @@ steps: Contents: cuemon.snk TargetFolder: '$(System.DefaultWorkingDirectory)\$(BuildSource)' -# INSERT SONAR CLOUD PREPARE HERE +- task: SonarCloudPrepare@1 + inputs: + SonarCloud: '$env:SONAR_CONNECTION' + organization: '$env:SONAR_ORGANIZATION' + scannerMode: 'MSBuild' + projectKey: '$env:SONAR_PROJECTKEY' + env: + SONAR_CONNECTION: $(sonarConnection) + SONAR_ORGANIZATION: $(sonarOrganization) + SONAR_PROJECTKEY: $(sonarProjectKey) - task: DotNetCoreCLI@2 displayName: 'Build netcoreapp3.0' @@ -78,10 +87,11 @@ steps: projects: 'test/**/*.csproj' arguments: '--configuration $(BuildConfiguration) --collect "Code coverage" ' -# INSERT SONAR CLOUD RUN ANALYSIS - +- task: SonarCloudAnalyze@1 -# INSERT SONAR CLOUD PUBLISH QUALITY GATE RESULT +- task: SonarCloudPublish@1 + inputs: + pollingTimeoutSec: '300' - task: PublishBuildArtifacts@1 displayName: 'Publish Artifact: Cuemon' From bf1588a834f3c6e466f737be8cb8ee512a3fb9e6 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 27 Aug 2020 23:17:31 +0200 Subject: [PATCH 040/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 34734b89d..48544659f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -34,7 +34,7 @@ steps: - task: DownloadSecureFile@1 displayName: 'Download cuemon.snk' inputs: - secureFile: '15606b59-2e5f-4cf8-a50d-66fa026bb391' + secureFile: 'cuemon.snk' - task: CopyFiles@2 displayName: 'Copy cuemon.snk to $(System.DefaultWorkingDirectory)\$(BuildSource)' From 5d9f97628f51631e0865ff415b00912e4cf973bb Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 27 Aug 2020 23:18:51 +0200 Subject: [PATCH 041/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 48544659f..0340f3c33 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -45,10 +45,10 @@ steps: - task: SonarCloudPrepare@1 inputs: - SonarCloud: '$env:SONAR_CONNECTION' - organization: '$env:SONAR_ORGANIZATION' + SonarCloud: $env:SONAR_CONNECTION + organization: $env:SONAR_ORGANIZATION scannerMode: 'MSBuild' - projectKey: '$env:SONAR_PROJECTKEY' + projectKey: $env:SONAR_PROJECTKEY env: SONAR_CONNECTION: $(sonarConnection) SONAR_ORGANIZATION: $(sonarOrganization) From 1e1f03356cbaa5eaf771303bc47eaf4af86041f1 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 27 Aug 2020 23:24:31 +0200 Subject: [PATCH 042/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0340f3c33..0b3af450a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,6 +1,11 @@ trigger: - development +variables: + SONAR_CONNECTION: $(sonarConnection) + SONAR_ORGANIZATION: $(sonarOrganization) + SONAR_PROJECTKEY: $(sonarProjectKey) + pool: vmImage: 'ubuntu-latest' @@ -45,14 +50,10 @@ steps: - task: SonarCloudPrepare@1 inputs: - SonarCloud: $env:SONAR_CONNECTION - organization: $env:SONAR_ORGANIZATION + SonarCloud: $(SONAR_CONNECTION) + organization: $(SONAR_ORGANIZATION) scannerMode: 'MSBuild' - projectKey: $env:SONAR_PROJECTKEY - env: - SONAR_CONNECTION: $(sonarConnection) - SONAR_ORGANIZATION: $(sonarOrganization) - SONAR_PROJECTKEY: $(sonarProjectKey) + projectKey: $(SONAR_PROJECTKEY) - task: DotNetCoreCLI@2 displayName: 'Build netcoreapp3.0' From feded85aa1805644f79cbae943434cee1a499ce9 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 27 Aug 2020 23:35:39 +0200 Subject: [PATCH 043/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0b3af450a..37f8326fe 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -50,10 +50,10 @@ steps: - task: SonarCloudPrepare@1 inputs: - SonarCloud: $(SONAR_CONNECTION) - organization: $(SONAR_ORGANIZATION) + SonarCloud: '$(SONAR_CONNECTION)' + organization: '$(SONAR_ORGANIZATION)' scannerMode: 'MSBuild' - projectKey: $(SONAR_PROJECTKEY) + projectKey: '$(SONAR_PROJECTKEY)' - task: DotNetCoreCLI@2 displayName: 'Build netcoreapp3.0' From 46375e2f81678c18d7a89b59b28572c157d67a87 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 27 Aug 2020 23:47:27 +0200 Subject: [PATCH 044/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 37f8326fe..7c0fc6bae 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -50,7 +50,7 @@ steps: - task: SonarCloudPrepare@1 inputs: - SonarCloud: '$(SONAR_CONNECTION)' + SonarCloud: 'Cuemon-SonarCloud' organization: '$(SONAR_ORGANIZATION)' scannerMode: 'MSBuild' projectKey: '$(SONAR_PROJECTKEY)' From 08cf462e613cb8b47784944a62857eda89e88201 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 27 Aug 2020 23:50:39 +0200 Subject: [PATCH 045/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7c0fc6bae..f23260281 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -2,12 +2,11 @@ trigger: - development variables: - SONAR_CONNECTION: $(sonarConnection) SONAR_ORGANIZATION: $(sonarOrganization) SONAR_PROJECTKEY: $(sonarProjectKey) pool: - vmImage: 'ubuntu-latest' + vmImage: 'windows-2019' steps: - task: UseDotNet@2 From d96d697bf69875bf31e0dcb82ac1cc4d1222e7d5 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 27 Aug 2020 23:55:50 +0200 Subject: [PATCH 046/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f23260281..1b9a0d1d8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -33,7 +33,7 @@ steps: displayName: Restore inputs: command: restore - projects: '$(Parameters.projects)' + projects: '**/*.csproj' - task: DownloadSecureFile@1 displayName: 'Download cuemon.snk' From 3968c6b313a7a26d0479ada976dafb79a2e793a4 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 28 Aug 2020 00:04:40 +0200 Subject: [PATCH 047/385] Update azure-pipelines.yml for Azure Pipelines Could not get secrets to work :-( --- azure-pipelines.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1b9a0d1d8..c3e4109b9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,10 +1,6 @@ trigger: - development -variables: - SONAR_ORGANIZATION: $(sonarOrganization) - SONAR_PROJECTKEY: $(sonarProjectKey) - pool: vmImage: 'windows-2019' @@ -50,9 +46,10 @@ steps: - task: SonarCloudPrepare@1 inputs: SonarCloud: 'Cuemon-SonarCloud' - organization: '$(SONAR_ORGANIZATION)' + organization: 'geekle' scannerMode: 'MSBuild' - projectKey: '$(SONAR_PROJECTKEY)' + projectKey: 'Cuemon' + projectName: 'Cuemon' - task: DotNetCoreCLI@2 displayName: 'Build netcoreapp3.0' From e23240b7bc5a59c9452af0611fa59fda1d105dac Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 28 Aug 2020 00:22:51 +0200 Subject: [PATCH 048/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c3e4109b9..86bb736fc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,6 +1,11 @@ trigger: - development +variables: + BuildSource: 'src' + BuildPlatform: 'Any CPU' + BuildConfiguration: 'Debug' + pool: vmImage: 'windows-2019' From 1d608bba4c01a8af56c3bee98b7cd171d89bc77d Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 28 Aug 2020 00:36:05 +0200 Subject: [PATCH 049/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 86bb736fc..643687e3c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -42,11 +42,11 @@ steps: secureFile: 'cuemon.snk' - task: CopyFiles@2 - displayName: 'Copy cuemon.snk to $(System.DefaultWorkingDirectory)\$(BuildSource)' + displayName: 'Copy cuemon.snk to $(System.DefaultWorkingDirectory)' inputs: SourceFolder: '$(Agent.TempDirectory)' Contents: cuemon.snk - TargetFolder: '$(System.DefaultWorkingDirectory)\$(BuildSource)' + TargetFolder: '$(System.DefaultWorkingDirectory)' - task: SonarCloudPrepare@1 inputs: From 73b7a92630ae667c588098bc3fca4b38e8e1ffb8 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 00:37:55 +0200 Subject: [PATCH 050/385] Testing out TF_BUILD variable. --- Directory.Build.props | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 41c01556c..07bfd1719 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,6 +4,16 @@ $(MSBuildProjectName.EndsWith('Tests')) + + true + true + $(System.DefaultWorkingDirectory)\cuemon.snk + + + + ..\cuemon.snk + + Copyright © Geekle 2009-2020. All rights reserved. Michael Mortensen @@ -18,12 +28,11 @@ true true true - ..\cuemon.snk true ..\..\.sonarlint\cuemoncorecsharp.ruleset - + From 93fc7b8f928fd46abb6256867f0eaf20660c3b8b Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 28 Aug 2020 00:48:17 +0200 Subject: [PATCH 051/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 643687e3c..913304305 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -30,12 +30,6 @@ steps: - script: 'nbgv cloud' displayName: 'Set Version using NBGV' -- task: DotNetCoreCLI@2 - displayName: Restore - inputs: - command: restore - projects: '**/*.csproj' - - task: DownloadSecureFile@1 displayName: 'Download cuemon.snk' inputs: @@ -48,6 +42,12 @@ steps: Contents: cuemon.snk TargetFolder: '$(System.DefaultWorkingDirectory)' +- task: DotNetCoreCLI@2 + displayName: Restore + inputs: + command: restore + projects: '**/*.csproj' + - task: SonarCloudPrepare@1 inputs: SonarCloud: 'Cuemon-SonarCloud' From 575d35291f4ac785e84e94689a2f90e22fcfb960 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 00:52:49 +0200 Subject: [PATCH 052/385] For some reasons, System.DefaultWorkingDirectory dit not work. --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 07bfd1719..31e546a99 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,7 +7,7 @@ true true - $(System.DefaultWorkingDirectory)\cuemon.snk + cuemon.snk From 2380afac48e9b9050e751f77183d1d0761a36552 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 01:06:10 +0200 Subject: [PATCH 053/385] Utilized MSBuildThisFileDirectory --- Directory.Build.props | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 31e546a99..c4f7e847b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,11 +7,6 @@ true true - cuemon.snk - - - - ..\cuemon.snk @@ -29,6 +24,7 @@ true true true + $(MSBuildThisFileDirectory)cuemon.snk ..\..\.sonarlint\cuemoncorecsharp.ruleset From ca3125e825178ba63a4bcbe05f3d5e48a1aa0670 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 01:07:22 +0200 Subject: [PATCH 054/385] Integration error. --- azure-pipelines-1.yml | 89 ------------------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 azure-pipelines-1.yml diff --git a/azure-pipelines-1.yml b/azure-pipelines-1.yml deleted file mode 100644 index bdca3ad78..000000000 --- a/azure-pipelines-1.yml +++ /dev/null @@ -1,89 +0,0 @@ -trigger: -- development - -pool: - vmImage: 'ubuntu-latest' - -steps: -- task: UseDotNet@2 - displayName: 'Use .Net Core SDK 2.2.110 (SonarCloud)' - inputs: - version: 2.2.110 - -- task: UseDotNet@2 - displayName: 'Use .Net Core 3.1.302' - inputs: - version: 3.1.302 - -- task: DotNetCoreCLI@2 - displayName: 'Install NBGV tool' - inputs: - command: custom - custom: tool - arguments: 'install --global nbgv' - -- script: 'nbgv cloud' - displayName: 'Set Version using NBGV' - -- task: DotNetCoreCLI@2 - displayName: Restore - inputs: - command: restore - projects: '$(Parameters.projects)' - -- task: DownloadSecureFile@1 - displayName: 'Download cuemon.snk' - inputs: - secureFile: '15606b59-2e5f-4cf8-a50d-66fa026bb391' - -- task: CopyFiles@2 - displayName: 'Copy cuemon.snk to $(System.DefaultWorkingDirectory)\$(BuildSource)' - inputs: - SourceFolder: '$(Agent.TempDirectory)' - Contents: cuemon.snk - TargetFolder: '$(System.DefaultWorkingDirectory)\$(BuildSource)' - -# INSERT SONAR CLOUD PREPARE HERE - -- task: DotNetCoreCLI@2 - displayName: 'Build netcoreapp3.0' - inputs: - projects: | - src/**/Cuemon.AspNetCore*.csproj - src/**/Cuemon.Extensions.AspNetCore*.csproj - src/**/Cuemon.Extensions.Xunit.csproj - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netcoreapp3.0 --framework netcoreapp3.0' - workingDirectory: '$(BuildSource)' - -- task: DotNetCoreCLI@2 - displayName: 'Build netstandard2.1' - inputs: - projects: | - src/**/Cuemon.Extensions.IO.csproj - src/**/Cuemon.IO.csproj - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.1 --framework netstandard2.1' - workingDirectory: '$(BuildSource)' - -- task: DotNetCoreCLI@2 - displayName: 'Build netstandard2.0' - inputs: - projects: 'src/**/*.csproj' - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.0 --framework netstandard2.0' - workingDirectory: '$(BuildSource)' - -- task: DotNetCoreCLI@2 - displayName: Test - inputs: - command: test - projects: 'test/**/*.csproj' - arguments: '--configuration $(BuildConfiguration) --collect "Code coverage" ' - -# INSERT SONAR CLOUD RUN ANALYSIS - - -# INSERT SONAR CLOUD PUBLISH QUALITY GATE RESULT - -- task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: Cuemon' - inputs: - ArtifactName: Cuemon \ No newline at end of file From d217d2b340c28633132eb93bcd3e6f06c804825d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 01:27:14 +0200 Subject: [PATCH 055/385] Tweaked to new name; Cuemon. --- README.md | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index f4c1b6b5e..3764f0cf4 100644 --- a/README.md +++ b/README.md @@ -4,26 +4,30 @@ Cuemon -------------------- Cuemon is a free and flexible assembly package for the Microsoft .NET ecosystem. It was built to extend and boost your codebelt - providing vast ways of possibilities for all breeds of coders, programmers, developers and the likes thereof. Ideal for .NET, .NET Standard, .NET Core, Universal Windows Platform and .NET Framework 4.6.1 and newer. -![License](https://img.shields.io/github/license/gimlichael/cuemoncore) +![License](https://img.shields.io/github/license/gimlichael/cuemon) This development branch contains the latest version which has been completely refactored and updated to suport .NET Core 3.1. -An Azure DevOps pipeline is currently in progress. +All CI and CD will be runned on Azure DevOps and is currently in process of being tweaked. -Once automated and tested thoroughly, it will be pushed to a new branch, release, and hereafter again tested and lastly to master and Nuget packages. +Once fully automated and tested thoroughly, it will be pushed to a new branch, release, and hereafter again tested and lastly to master and Nuget packages. -[![Build Status](https://gimlichael.visualstudio.com/CuemonCore/_apis/build/status/CuemonCore%20-%20Development%20-%20CI?branchName=development)](https://gimlichael.visualstudio.com/CuemonCore/_build/latest?definitionId=1&branchName=development) +Another big change for this upcoming release is the versioning; the world has spoken - and chosen semantic versioning. -[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=CuemonCore&metric=bugs)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=CuemonCore&metric=code_smells)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=CuemonCore&metric=coverage)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=CuemonCore&metric=ncloc)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=CuemonCore&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=CuemonCore&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=CuemonCore&metric=security_rating)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=CuemonCore&metric=sqale_index)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=CuemonCore&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=CuemonCore) +The release for now is planned to be 6.0.0. -[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=CuemonCore)](https://sonarcloud.io/dashboard?id=CuemonCore) +[![Build Status](https://dev.azure.com/gimlichael/Cuemon/_apis/build/status/gimlichael.Cuemon?branchName=development)](https://dev.azure.com/gimlichael/Cuemon/_build/latest?definitionId=9&branchName=development) + +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=bugs)](https://sonarcloud.io/dashboard?id=CuemonCore) +[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=code_smells)](https://sonarcloud.io/dashboard?id=CuemonCore) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=coverage)](https://sonarcloud.io/dashboard?id=CuemonCore) +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=ncloc)](https://sonarcloud.io/dashboard?id=CuemonCore) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=CuemonCore) +[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=CuemonCore) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=security_rating)](https://sonarcloud.io/dashboard?id=CuemonCore) +[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_index)](https://sonarcloud.io/dashboard?id=CuemonCore) +[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=CuemonCore) + +[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=Cuemon)](https://sonarcloud.io/dashboard?id=CuemonCore) Stay tuned! From 0a3c354f23058132232923a362ca139a2ca652b4 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 01:37:48 +0200 Subject: [PATCH 056/385] Fixed PublishBuildArtifacts --- azure-pipelines.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 913304305..d07eed2f4 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -96,6 +96,7 @@ steps: pollingTimeoutSec: '300' - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: Cuemon' inputs: - ArtifactName: Cuemon \ No newline at end of file + PathtoPublish: '$(Build.ArtifactStagingDirectory)' + ArtifactName: 'Cuemon' + publishLocation: 'Container' \ No newline at end of file From edeb34a202cabc154118b15004bafaff6124e85b Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 02:12:09 +0200 Subject: [PATCH 057/385] SourceRoot items must include at least one top-level (not nested) item when DeterministicSourcePaths is true. --- Directory.Build.props | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Directory.Build.props b/Directory.Build.props index c4f7e847b..a282920cb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,6 +42,10 @@ + + + + netcoreapp3.0 false From c206a1ee3555f5f96d0c9e3af80ed8c33345217b Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 02:40:13 +0200 Subject: [PATCH 058/385] Minor code-smell cleanup. --- .../Filters/Cacheable/HttpCacheableFilter.cs | 2 +- .../Filters/Cacheable/HttpEntityTagHeaderFilter.cs | 5 ++--- .../Filters/Cacheable/HttpEntityTagHeaderOptions.cs | 1 - .../Cacheable/HttpLastModifiedHeaderFilter.cs | 1 - .../Cacheable/HttpLastModifiedHeaderOptions.cs | 1 - src/Cuemon.AspNetCore.Mvc/GlobalSuppressions.cs | 8 ++++++++ src/Cuemon.AspNetCore.Mvc/TimeBasedObjectResult.cs | 1 - .../Http/Throttling/ThrottlingSentinelMiddleware.cs | 12 ++++++------ src/Cuemon.Data/DataTransferRow.cs | 1 - src/Cuemon.Data/DataTransferRowCollection.cs | 1 - 10 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 src/Cuemon.AspNetCore.Mvc/GlobalSuppressions.cs diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpCacheableFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpCacheableFilter.cs index 1bb4878c9..f3ecbacdf 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpCacheableFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpCacheableFilter.cs @@ -30,7 +30,7 @@ public override async Task OnResultExecutionAsync(ResultExecutingContext context { foreach (var filter in Options.Filters) { - await filter.OnResultExecutionAsync(context, next); + await filter.OnResultExecutionAsync(context, next).ConfigureAwait(false); } if (context.Result is ObjectResult result && result.Value is ICacheableObjectResult cacheableObjectResult) diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderFilter.cs index ff8578238..9dae8427b 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderFilter.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Cuemon.AspNetCore.Http; using Cuemon.Configuration; -using Cuemon.Data; using Cuemon.Data.Integrity; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -61,12 +60,12 @@ public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultE originalValue = result.Value; result.Value = cacheableObjectResult.Value; } - await InvokeEntityTagResponseParser(context, next, statusCodeBeforeBodyRead); + await InvokeEntityTagResponseParser(context, next, statusCodeBeforeBodyRead).ConfigureAwait(false); result.Value = originalValue; } else { - await InvokeEntityTagResponseParser(context, next, statusCodeBeforeBodyRead); + await InvokeEntityTagResponseParser(context, next, statusCodeBeforeBodyRead).ConfigureAwait(false); } } } diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs index 31799ce30..135d41d50 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs @@ -1,7 +1,6 @@ using System; using System.IO; using Cuemon.AspNetCore.Http; -using Cuemon.Data; using Cuemon.Data.Integrity; using Cuemon.Security.Cryptography; using Microsoft.AspNetCore.Http; diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderFilter.cs index c932c0b3b..3220fd154 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderFilter.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Cuemon.AspNetCore.Http; using Cuemon.Configuration; -using Cuemon.Data; using Cuemon.Data.Integrity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs index 617a860b5..c4f2ad8f3 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs @@ -1,6 +1,5 @@ using System; using Cuemon.AspNetCore.Http; -using Cuemon.Data; using Cuemon.Data.Integrity; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/src/Cuemon.AspNetCore.Mvc/GlobalSuppressions.cs b/src/Cuemon.AspNetCore.Mvc/GlobalSuppressions.cs new file mode 100644 index 000000000..6b988a9ce --- /dev/null +++ b/src/Cuemon.AspNetCore.Mvc/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Major Code Smell", "S3358:Ternary operators should not be nested", Justification = "Clear enough.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Mvc.ContentBasedObjectResult.#ctor(System.Object,System.Byte[],System.Boolean)")] diff --git a/src/Cuemon.AspNetCore.Mvc/TimeBasedObjectResult.cs b/src/Cuemon.AspNetCore.Mvc/TimeBasedObjectResult.cs index aa750b4eb..6e5631f93 100644 --- a/src/Cuemon.AspNetCore.Mvc/TimeBasedObjectResult.cs +++ b/src/Cuemon.AspNetCore.Mvc/TimeBasedObjectResult.cs @@ -1,5 +1,4 @@ using System; -using Cuemon.Data; using Cuemon.Data.Integrity; namespace Cuemon.AspNetCore.Mvc diff --git a/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs b/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs index 159dce8c9..f63526dbb 100644 --- a/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs +++ b/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs @@ -35,19 +35,19 @@ public ThrottlingSentinelMiddleware(RequestDelegate next, Action. /// /// The context of the current request. - /// The dependency injected of . + /// The dependency injected of . /// A task that represents the execution of this middleware. - public override async Task InvokeAsync(HttpContext context, IThrottlingCache tc) + public override async Task InvokeAsync(HttpContext context, IThrottlingCache di) { var exception = false; try { - await AspNetCoreInfrastructure.InvokeThrottlerSentinelAsync(context, tc, Options, async (message, response) => + await AspNetCoreInfrastructure.InvokeThrottlerSentinelAsync(context, di, Options, async (message, response) => { - response.StatusCode = (int) message.StatusCode; + response.StatusCode = (int)message.StatusCode; Decorator.Enclose(response.Headers).TryAddOrUpdateHeaders(message.Headers); - await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync()); - }); + await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); + }).ConfigureAwait(false); } catch (ThrottlingException) { diff --git a/src/Cuemon.Data/DataTransferRow.cs b/src/Cuemon.Data/DataTransferRow.cs index 6f7497b2f..c03a9c137 100644 --- a/src/Cuemon.Data/DataTransferRow.cs +++ b/src/Cuemon.Data/DataTransferRow.cs @@ -1,6 +1,5 @@ using System.Globalization; using System.Text; -using Cuemon.Reflection; namespace Cuemon.Data { diff --git a/src/Cuemon.Data/DataTransferRowCollection.cs b/src/Cuemon.Data/DataTransferRowCollection.cs index 591b6813a..d71243346 100644 --- a/src/Cuemon.Data/DataTransferRowCollection.cs +++ b/src/Cuemon.Data/DataTransferRowCollection.cs @@ -4,7 +4,6 @@ using System.Collections.ObjectModel; using System.Data; using System.Linq; -using Cuemon.Reflection; namespace Cuemon.Data { From 5183c84ff0a451f02248d1cd3f032433f160165f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 02:45:45 +0200 Subject: [PATCH 059/385] Bugfix --- .../MvcCoreBuilderExtensions.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcCoreBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcCoreBuilderExtensions.cs index 4be397e3d..efddefe38 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcCoreBuilderExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/MvcCoreBuilderExtensions.cs @@ -39,12 +39,14 @@ public static IMvcCoreBuilder AddJsonSerializationFormatters(this IMvcCoreBuilde /// The which need to be configured. /// A reference to after the operation has completed. /// - /// cannot be null. + /// cannot be null -or- + /// cannot be null. /// public static IMvcCoreBuilder AddJsonSerializationFormatters(this IMvcCoreBuilder builder, Action setup) { Validator.ThrowIfNull(builder, nameof(builder)); - builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, JsonSerializationMvcOptionsSetup>()); + AddJsonSerializationFormatters(builder); + AddJsonFormatterOptions(builder, setup); return builder; } From 07b226f4da702483d72c505182c81eed011c4798 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 03:05:29 +0200 Subject: [PATCH 060/385] Justified S3776, S2436 and S107. --- .../GlobalSuppressions.cs | 8 ++++++++ src/Cuemon.Resilience/GlobalSuppressions.cs | 12 ++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/GlobalSuppressions.cs diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/GlobalSuppressions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/GlobalSuppressions.cs new file mode 100644 index 000000000..7e95c24bf --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Critical Code Smell", "S3776:Cognitive Complexity of methods should not be too high", Justification = "Clear enough; XML Converter.", Scope = "member", Target = "~M:Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.XmlConverterExtensions.AddHttpExceptionDescriptorConverter(System.Collections.Generic.IList{Cuemon.Xml.Serialization.Converters.XmlConverter},System.Action{Cuemon.Diagnostics.ExceptionDescriptorOptions})~System.Collections.Generic.IList{Cuemon.Xml.Serialization.Converters.XmlConverter}")] diff --git a/src/Cuemon.Resilience/GlobalSuppressions.cs b/src/Cuemon.Resilience/GlobalSuppressions.cs index e1574ea48..2e5d5cce5 100644 --- a/src/Cuemon.Resilience/GlobalSuppressions.cs +++ b/src/Cuemon.Resilience/GlobalSuppressions.cs @@ -14,3 +14,15 @@ [assembly: SuppressMessage("Major Code Smell", "S1854:Unused assignments should be removed", Justification = "False-positive.", Scope = "member", Target = "~M:Cuemon.Resilience.AsyncFuncTransientWorker`1.ResilientFuncAsync(System.Func{System.Threading.CancellationToken,System.Threading.Tasks.Task{`0}},System.Threading.CancellationToken)~System.Threading.Tasks.Task{`0}")] [assembly: SuppressMessage("Major Code Smell", "S1854:Unused assignments should be removed", Justification = "False-positive.", Scope = "member", Target = "~M:Cuemon.Resilience.FuncTransientWorker`1.ResilientFunc(System.Func{`0})~`0")] [assembly: SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "MethodBase and Type are not serializable; hence the workaround.", Scope = "type", Target = "~T:Cuemon.Resilience.TransientFaultEvidence")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; generic typed parameters.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithActionAsync``4(System.Func{``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,System.Threading.CancellationToken,System.Action{Cuemon.Resilience.TransientOperationOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; generic typed parameters.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithAction``4(System.Action{``0,``1,``2,``3},``0,``1,``2,``3,System.Action{Cuemon.Resilience.TransientOperationOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; generic typed parameters.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithAction``5(System.Action{``0,``1,``2,``3,``4},``0,``1,``2,``3,``4,System.Action{Cuemon.Resilience.TransientOperationOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; generic typed parameters.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithActionAsync``5(System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Action{Cuemon.Resilience.TransientOperationOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; generic typed parameters.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithActionAsync``5(System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Action{Cuemon.Resilience.TransientOperationOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; generic typed parameters.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithFunc``4(System.Func{``0,``1,``2,``3},``0,``1,``2,System.Action{Cuemon.Resilience.TransientOperationOptions})~``3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; generic typed parameters.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithFunc``5(System.Func{``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{Cuemon.Resilience.TransientOperationOptions})~``4")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; generic typed parameters.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithFunc``6(System.Func{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{Cuemon.Resilience.TransientOperationOptions})~``5")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; generic typed parameters.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithFuncAsync``4(System.Func{``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2,System.Threading.CancellationToken,System.Action{Cuemon.Resilience.TransientOperationOptions})~System.Threading.Tasks.Task{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; generic typed parameters.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithFuncAsync``5(System.Func{``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Action{Cuemon.Resilience.TransientOperationOptions})~System.Threading.Tasks.Task{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; generic typed parameters for.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithFuncAsync``6(System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Action{Cuemon.Resilience.TransientOperationOptions})~System.Threading.Tasks.Task{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; generic typed parameters.", Scope = "member", Target = "~M:Cuemon.Resilience.TransientOperation.WithFuncAsync``6(System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Action{Cuemon.Resilience.TransientOperationOptions})~System.Threading.Tasks.Task{``5}")] From 8aa4f141179274a2cea96f89707644a8192bd6bb Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 03:24:13 +0200 Subject: [PATCH 061/385] Has to increase the buildtime because of SonarCloud and Test. --- azure-pipelines.yml | 192 ++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 94 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d07eed2f4..ed9ef01dd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,97 +6,101 @@ variables: BuildPlatform: 'Any CPU' BuildConfiguration: 'Debug' -pool: - vmImage: 'windows-2019' - -steps: -- task: UseDotNet@2 - displayName: 'Use .Net Core SDK 2.2.110 (SonarCloud)' - inputs: - version: 2.2.110 - -- task: UseDotNet@2 - displayName: 'Use .Net Core 3.1.302' - inputs: - version: 3.1.302 - -- task: DotNetCoreCLI@2 - displayName: 'Install NBGV tool' - inputs: - command: custom - custom: tool - arguments: 'install --global nbgv' - -- script: 'nbgv cloud' - displayName: 'Set Version using NBGV' - -- task: DownloadSecureFile@1 - displayName: 'Download cuemon.snk' - inputs: - secureFile: 'cuemon.snk' - -- task: CopyFiles@2 - displayName: 'Copy cuemon.snk to $(System.DefaultWorkingDirectory)' - inputs: - SourceFolder: '$(Agent.TempDirectory)' - Contents: cuemon.snk - TargetFolder: '$(System.DefaultWorkingDirectory)' - -- task: DotNetCoreCLI@2 - displayName: Restore - inputs: - command: restore - projects: '**/*.csproj' - -- task: SonarCloudPrepare@1 - inputs: - SonarCloud: 'Cuemon-SonarCloud' - organization: 'geekle' - scannerMode: 'MSBuild' - projectKey: 'Cuemon' - projectName: 'Cuemon' - -- task: DotNetCoreCLI@2 - displayName: 'Build netcoreapp3.0' - inputs: - projects: | - src/**/Cuemon.AspNetCore*.csproj - src/**/Cuemon.Extensions.AspNetCore*.csproj - src/**/Cuemon.Extensions.Xunit.csproj - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netcoreapp3.0 --framework netcoreapp3.0' - workingDirectory: '$(BuildSource)' - -- task: DotNetCoreCLI@2 - displayName: 'Build netstandard2.1' - inputs: - projects: | - src/**/Cuemon.Extensions.IO.csproj - src/**/Cuemon.IO.csproj - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.1 --framework netstandard2.1' - workingDirectory: '$(BuildSource)' - -- task: DotNetCoreCLI@2 - displayName: 'Build netstandard2.0' - inputs: - projects: 'src/**/*.csproj' - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.0 --framework netstandard2.0' - workingDirectory: '$(BuildSource)' - -- task: DotNetCoreCLI@2 - displayName: Test - inputs: - command: test - projects: 'test/**/*.csproj' - arguments: '--configuration $(BuildConfiguration) --collect "Code coverage" ' - -- task: SonarCloudAnalyze@1 - -- task: SonarCloudPublish@1 - inputs: - pollingTimeoutSec: '300' - -- task: PublishBuildArtifacts@1 - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)' - ArtifactName: 'Cuemon' - publishLocation: 'Container' \ No newline at end of file +jobs: +- job: CI + timeoutInMinutes: 360 + + pool: + vmImage: 'windows-2019' + + steps: + - task: UseDotNet@2 + displayName: 'Use .Net Core SDK 2.2.110 (SonarCloud)' + inputs: + version: 2.2.110 + + - task: UseDotNet@2 + displayName: 'Use .Net Core 3.1.302' + inputs: + version: 3.1.302 + + - task: DotNetCoreCLI@2 + displayName: 'Install NBGV tool' + inputs: + command: custom + custom: tool + arguments: 'install --global nbgv' + + - script: 'nbgv cloud' + displayName: 'Set Version using NBGV' + + - task: DownloadSecureFile@1 + displayName: 'Download cuemon.snk' + inputs: + secureFile: 'cuemon.snk' + + - task: CopyFiles@2 + displayName: 'Copy cuemon.snk to $(System.DefaultWorkingDirectory)' + inputs: + SourceFolder: '$(Agent.TempDirectory)' + Contents: cuemon.snk + TargetFolder: '$(System.DefaultWorkingDirectory)' + + - task: DotNetCoreCLI@2 + displayName: Restore + inputs: + command: restore + projects: '**/*.csproj' + + - task: SonarCloudPrepare@1 + inputs: + SonarCloud: 'Cuemon-SonarCloud' + organization: 'geekle' + scannerMode: 'MSBuild' + projectKey: 'Cuemon' + projectName: 'Cuemon' + + - task: DotNetCoreCLI@2 + displayName: 'Build netcoreapp3.0' + inputs: + projects: | + src/**/Cuemon.AspNetCore*.csproj + src/**/Cuemon.Extensions.AspNetCore*.csproj + src/**/Cuemon.Extensions.Xunit.csproj + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netcoreapp3.0 --framework netcoreapp3.0' + workingDirectory: '$(BuildSource)' + + - task: DotNetCoreCLI@2 + displayName: 'Build netstandard2.1' + inputs: + projects: | + src/**/Cuemon.Extensions.IO.csproj + src/**/Cuemon.IO.csproj + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.1 --framework netstandard2.1' + workingDirectory: '$(BuildSource)' + + - task: DotNetCoreCLI@2 + displayName: 'Build netstandard2.0' + inputs: + projects: 'src/**/*.csproj' + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.0 --framework netstandard2.0' + workingDirectory: '$(BuildSource)' + + - task: DotNetCoreCLI@2 + displayName: Test + inputs: + command: test + projects: 'test/**/*.csproj' + arguments: '--configuration $(BuildConfiguration) --collect "Code coverage" ' + + - task: SonarCloudAnalyze@1 + + - task: SonarCloudPublish@1 + inputs: + pollingTimeoutSec: '300' + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)' + ArtifactName: 'Cuemon' + publishLocation: 'Container' \ No newline at end of file From 538e88a1b987973eda55e4f82b2a892294be7d89 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 03:26:37 +0200 Subject: [PATCH 062/385] Indent error. --- azure-pipelines.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ed9ef01dd..0ea421fee 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -64,9 +64,9 @@ jobs: displayName: 'Build netcoreapp3.0' inputs: projects: | - src/**/Cuemon.AspNetCore*.csproj - src/**/Cuemon.Extensions.AspNetCore*.csproj - src/**/Cuemon.Extensions.Xunit.csproj + src/**/Cuemon.AspNetCore*.csproj + src/**/Cuemon.Extensions.AspNetCore*.csproj + src/**/Cuemon.Extensions.Xunit.csproj arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netcoreapp3.0 --framework netcoreapp3.0' workingDirectory: '$(BuildSource)' @@ -74,8 +74,8 @@ jobs: displayName: 'Build netstandard2.1' inputs: projects: | - src/**/Cuemon.Extensions.IO.csproj - src/**/Cuemon.IO.csproj + src/**/Cuemon.Extensions.IO.csproj + src/**/Cuemon.IO.csproj arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.1 --framework netstandard2.1' workingDirectory: '$(BuildSource)' From 7cf25862d1526ef5c14debb69a98900c77b68def Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 22:17:46 +0200 Subject: [PATCH 063/385] Tweking of pipeline --- Directory.Build.props | 16 +++++----------- README.md | 20 ++++++++++---------- azure-pipelines.yml | 5 +++-- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index a282920cb..9e6f4115a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,6 +9,10 @@ true + + ..\..\.sonarlint\cuemoncorecsharp.ruleset + + Copyright © Geekle 2009-2020. All rights reserved. Michael Mortensen @@ -25,15 +29,9 @@ true true $(MSBuildThisFileDirectory)cuemon.snk - ..\..\.sonarlint\cuemoncorecsharp.ruleset - - - - - - + @@ -59,10 +57,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/README.md b/README.md index 3764f0cf4..fe8df5a2c 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ The release for now is planned to be 6.0.0. [![Build Status](https://dev.azure.com/gimlichael/Cuemon/_apis/build/status/gimlichael.Cuemon?branchName=development)](https://dev.azure.com/gimlichael/Cuemon/_build/latest?definitionId=9&branchName=development) -[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=bugs)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=code_smells)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=coverage)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=ncloc)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=security_rating)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_index)](https://sonarcloud.io/dashboard?id=CuemonCore) -[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=CuemonCore) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=bugs)](https://sonarcloud.io/dashboard?id=Cuemon) +[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=code_smells)](https://sonarcloud.io/dashboard?id=Cuemon) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=coverage)](https://sonarcloud.io/dashboard?id=Cuemon) +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=ncloc)](https://sonarcloud.io/dashboard?id=Cuemon) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=Cuemon) +[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=Cuemon) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=security_rating)](https://sonarcloud.io/dashboard?id=Cuemon) +[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_index)](https://sonarcloud.io/dashboard?id=Cuemon) +[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=Cuemon) -[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=Cuemon)](https://sonarcloud.io/dashboard?id=CuemonCore) +[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=Cuemon)](https://sonarcloud.io/dashboard?id=Cuemon) Stay tuned! diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0ea421fee..07e3af593 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,7 +4,7 @@ trigger: variables: BuildSource: 'src' BuildPlatform: 'Any CPU' - BuildConfiguration: 'Debug' + BuildConfiguration: 'Release' jobs: - job: CI @@ -59,6 +59,7 @@ jobs: scannerMode: 'MSBuild' projectKey: 'Cuemon' projectName: 'Cuemon' + projectVersion: '$(Build.SourceVersion)' - task: DotNetCoreCLI@2 displayName: 'Build netcoreapp3.0' @@ -91,7 +92,7 @@ jobs: inputs: command: test projects: 'test/**/*.csproj' - arguments: '--configuration $(BuildConfiguration) --collect "Code coverage" ' + arguments: '--configuration $(BuildConfiguration) --collect "Code coverage"' - task: SonarCloudAnalyze@1 From b65b8a8c7c4657d39a5c49852fde16c00910e9df Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 28 Aug 2020 23:20:48 +0200 Subject: [PATCH 064/385] Lightweight for testing pipeline. --- azure-pipelines.yml | 53 +++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 07e3af593..e1043a2a0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,14 +15,14 @@ jobs: steps: - task: UseDotNet@2 - displayName: 'Use .Net Core SDK 2.2.110 (SonarCloud)' + displayName: 'Use .Net Core SDK 2.2.207 (SonarCloud)' inputs: - version: 2.2.110 + version: 2.2.207 - task: UseDotNet@2 - displayName: 'Use .Net Core 3.1.302' + displayName: 'Use .Net Core 3.1.401' inputs: - version: 3.1.302 + version: 3.1.401 - task: DotNetCoreCLI@2 displayName: 'Install NBGV tool' @@ -50,7 +50,7 @@ jobs: displayName: Restore inputs: command: restore - projects: '**/*.csproj' + projects: '**/Cuemon.Core.csproj' - task: SonarCloudPrepare@1 inputs: @@ -59,40 +59,23 @@ jobs: scannerMode: 'MSBuild' projectKey: 'Cuemon' projectName: 'Cuemon' - projectVersion: '$(Build.SourceVersion)' + projectVersion: '$(GitVersion.NuGetVersion)' - task: DotNetCoreCLI@2 - displayName: 'Build netcoreapp3.0' + displayName: 'Build and Pack Assemblies' inputs: - projects: | - src/**/Cuemon.AspNetCore*.csproj - src/**/Cuemon.Extensions.AspNetCore*.csproj - src/**/Cuemon.Extensions.Xunit.csproj - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netcoreapp3.0 --framework netcoreapp3.0' - workingDirectory: '$(BuildSource)' - - - task: DotNetCoreCLI@2 - displayName: 'Build netstandard2.1' - inputs: - projects: | - src/**/Cuemon.Extensions.IO.csproj - src/**/Cuemon.IO.csproj - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.1 --framework netstandard2.1' - workingDirectory: '$(BuildSource)' - - - task: DotNetCoreCLI@2 - displayName: 'Build netstandard2.0' - inputs: - projects: 'src/**/*.csproj' - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)\netstandard2.0 --framework netstandard2.0' - workingDirectory: '$(BuildSource)' + command: pack + packagesToPack: 'src/**/Cuemon.Core.csproj' + configuration: '$(BuildConfiguration)' + packDirectory: '$(Build.ArtifactStagingDirectory)\artifacts' + verbosityPack: Minimal - task: DotNetCoreCLI@2 displayName: Test inputs: command: test - projects: 'test/**/*.csproj' - arguments: '--configuration $(BuildConfiguration) --collect "Code coverage"' + projects: 'test/**/Cuemon.Core.Tests.csproj' + arguments: '--configuration $(BuildConfiguration) --collect "Code coverage"' - task: SonarCloudAnalyze@1 @@ -100,8 +83,6 @@ jobs: inputs: pollingTimeoutSec: '300' - - task: PublishBuildArtifacts@1 - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)' - ArtifactName: 'Cuemon' - publishLocation: 'Container' \ No newline at end of file + - publish: $(Build.ArtifactStagingDirectory)\artifacts + displayName: Publish Artifacts (Nuget) + artifact: BuildPackages \ No newline at end of file From 76a6f0197328ab81ec1e1f37db79cec8b2b2825b Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 29 Aug 2020 22:05:16 +0200 Subject: [PATCH 065/385] Pipepline and test adjustments (preparation for Ubuntu agent). --- Directory.Build.props | 16 +++-- azure-pipelines.yml | 15 ++-- .../Assets/UnmanagedDisposable.cs | 68 +++++++++++++------ test/Cuemon.Core.Tests/DisposableTest.cs | 1 + .../TypeDecoratorExtensionsTest.cs | 7 +- 5 files changed, 74 insertions(+), 33 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9e6f4115a..413d13217 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,8 +9,8 @@ true - - ..\..\.sonarlint\cuemoncorecsharp.ruleset + + ..\..\.sonarlint\cuemoncsharp.ruleset @@ -31,8 +31,8 @@ $(MSBuildThisFileDirectory)cuemon.snk - - + + @@ -57,6 +57,14 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e1043a2a0..4acefb81c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -50,39 +50,42 @@ jobs: displayName: Restore inputs: command: restore - projects: '**/Cuemon.Core.csproj' + projects: '**/*.csproj' - task: SonarCloudPrepare@1 + displayName: 'Prepare Analysis on SonarCloud' inputs: SonarCloud: 'Cuemon-SonarCloud' organization: 'geekle' scannerMode: 'MSBuild' projectKey: 'Cuemon' projectName: 'Cuemon' - projectVersion: '$(GitVersion.NuGetVersion)' + projectVersion: '$(GitAssemblyInformationalVersion)' - task: DotNetCoreCLI@2 displayName: 'Build and Pack Assemblies' inputs: command: pack - packagesToPack: 'src/**/Cuemon.Core.csproj' + packagesToPack: 'src/**/*.csproj' configuration: '$(BuildConfiguration)' - packDirectory: '$(Build.ArtifactStagingDirectory)\artifacts' + packDirectory: '$(Build.ArtifactStagingDirectory)/artifacts' verbosityPack: Minimal - task: DotNetCoreCLI@2 displayName: Test inputs: command: test - projects: 'test/**/Cuemon.Core.Tests.csproj' + projects: 'test/**/*.csproj' arguments: '--configuration $(BuildConfiguration) --collect "Code coverage"' - task: SonarCloudAnalyze@1 + displayName: 'Run Sonar Cloud Code Analysis' - task: SonarCloudPublish@1 + displayName: 'Publish Quality Gate Result to Sonar Cloud' inputs: pollingTimeoutSec: '300' - publish: $(Build.ArtifactStagingDirectory)\artifacts - displayName: Publish Artifacts (Nuget) + displayName: 'Publish Artifacts (Nuget)' artifact: BuildPackages \ No newline at end of file diff --git a/test/Cuemon.Core.Tests/Assets/UnmanagedDisposable.cs b/test/Cuemon.Core.Tests/Assets/UnmanagedDisposable.cs index 163e53893..443f6a119 100644 --- a/test/Cuemon.Core.Tests/Assets/UnmanagedDisposable.cs +++ b/test/Cuemon.Core.Tests/Assets/UnmanagedDisposable.cs @@ -5,33 +5,47 @@ namespace Cuemon.Assets { public class UnmanagedDisposable : FinalizeDisposable { - [DllImport("kernel32.dll", CharSet = CharSet.Auto, - CallingConvention = CallingConvention.StdCall, - SetLastError = true)] - public static extern IntPtr CreateFile( - string lpFileName, + internal IntPtr _handle = IntPtr.Zero; + internal IntPtr _libHandle = IntPtr.Zero; + + public delegate bool CloseHandle(IntPtr hObject); + + public delegate IntPtr CreateFileDelegate(string lpFileName, uint dwDesiredAccess, uint dwShareMode, - IntPtr SecurityAttributes, + IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, - IntPtr hTemplateFile - ); - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool CloseHandle(IntPtr hObject); + IntPtr hTemplateFile); - public IntPtr _handle = IntPtr.Zero; + public delegate IntPtr PtSname(int fd); public UnmanagedDisposable() { - _handle = CreateFile(@"C:\TestFile.txt", - 0x80000000, //access read-only - 1, //share-read - IntPtr.Zero, - 3, //open existing - 0, - IntPtr.Zero); + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + if (NativeLibrary.TryLoad("kernel32.dll", GetType().Assembly, DllImportSearchPath.System32, out _libHandle)) + { + if (NativeLibrary.TryGetExport(_libHandle, "CreateFileW", out var functionHandle)) + { + var createFileFunc = Marshal.GetDelegateForFunctionPointer(functionHandle); + _handle = createFileFunc(@"C:\TestFile.txt", + 0x80000000, //access read-only + 1, //share-read + IntPtr.Zero, + 3, //open existing + 0, + IntPtr.Zero); + } + } + } + else if (Environment.OSVersion.Platform == PlatformID.Unix) + { + if (NativeLibrary.TryLoad("libc.so.6", GetType().Assembly, DllImportSearchPath.SafeDirectories, out _libHandle)) + { + _handle = _libHandle; // i don't know of any native methods on unix + } + } } protected override void OnDisposeManagedResources() @@ -41,9 +55,21 @@ protected override void OnDisposeManagedResources() protected override void OnDisposeUnmanagedResources() { - if (_handle != IntPtr.Zero) + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + if (_handle != IntPtr.Zero) + { + if (NativeLibrary.TryGetExport(_libHandle, "CloseHandle", out var closeHandle)) + { + var closeHandleAction = Marshal.GetDelegateForFunctionPointer(closeHandle); + closeHandleAction(_handle); + } + } + NativeLibrary.Free(_libHandle); + } + else if (Environment.OSVersion.Platform == PlatformID.Unix) { - CloseHandle(_handle); + NativeLibrary.Free(_libHandle); } } } diff --git a/test/Cuemon.Core.Tests/DisposableTest.cs b/test/Cuemon.Core.Tests/DisposableTest.cs index 5b2289883..f2d0d8359 100644 --- a/test/Cuemon.Core.Tests/DisposableTest.cs +++ b/test/Cuemon.Core.Tests/DisposableTest.cs @@ -154,6 +154,7 @@ public void UnmanagedDisposable_VerifyThatAssetIsBeingDisposedOnFinalize() Action body = () => { var o = new UnmanagedDisposable(); + Assert.NotEqual(IntPtr.Zero, o._libHandle); Assert.NotEqual(IntPtr.Zero, o._handle); unmanaged = new WeakReference(o, true); }; diff --git a/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs index 999b770b5..697f88a26 100644 --- a/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs @@ -255,12 +255,15 @@ public void ToFriendlyName_ShouldProvideDefaultImplementationOfTypes() var defaultString = Decorator.Enclose(typeof(Tuple)).ToFriendlyName(); var fullNameString = Decorator.Enclose(typeof(Tuple)).ToFriendlyName(o => o.FullName = true); var noGenericsString = Decorator.Enclose(typeof(Tuple)).ToFriendlyName(o => o.ExcludeGenericArguments = true); - var seCultureInfo = Decorator.Enclose(typeof(Tuple)).ToFriendlyName(o => o.FormatProvider = CultureInfo.GetCultureInfo("se-SV")); Assert.Equal("Tuple", defaultString); Assert.Equal("System.Tuple", fullNameString); Assert.Equal("Tuple", noGenericsString); - Assert.Equal("Tuple", seCultureInfo); + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + var seCultureInfo = Decorator.Enclose(typeof(Tuple)).ToFriendlyName(o => o.FormatProvider = CultureInfo.GetCultureInfo("se-SV")); // unix has different culture interpretation + Assert.Equal("Tuple", seCultureInfo); + } } [Fact] From 68962e303536a7862cf286518370a3bb42e8b4b4 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Sat, 29 Aug 2020 23:00:37 +0200 Subject: [PATCH 066/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4acefb81c..17b153700 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -77,6 +77,7 @@ jobs: command: test projects: 'test/**/*.csproj' arguments: '--configuration $(BuildConfiguration) --collect "Code coverage"' + publishTestResults: true - task: SonarCloudAnalyze@1 displayName: 'Run Sonar Cloud Code Analysis' From 179e3246821a0d5d5e155e75aa4b9715866b5a4d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 13:08:01 +0200 Subject: [PATCH 067/385] CI builds messes with our unit test. --- .../Reflection/AssemblyDecoratorExtensionsTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs index 37c061fa1..2adaa5507 100644 --- a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs @@ -36,7 +36,7 @@ public void GetTypes_ShouldReturnAllTypesFromCuemonCore() var disposableTypesCount = Decorator.Enclose(disposableTypes).Inner.Count(); var configurationTypesCount = Decorator.Enclose(configurationTypes).Inner.Count(); - Assert.Equal(528, allTypesCount); + Assert.InRange(allTypesCount, 525, 530); // range because of tooling on CI adding dynamic types Assert.Equal(7, disposableTypesCount); Assert.Equal(2, configurationTypesCount); } From 22dc1f56a9f340bb50b7dbb71122f23a9b9a1791 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 13:08:17 +0200 Subject: [PATCH 068/385] Renamed folder to be consistent with project name. --- Cuemon.sln | 2 +- .../AssemblyExtensionsTest.cs | 0 .../Cuemon.Extensions.Data.Integrity.Tests.csproj | 0 .../DateTimeExtensionsTest.cs | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename test/{Cuemon.Extensions.Integrity.Tests => Cuemon.Extensions.Data.Integrity.Tests}/AssemblyExtensionsTest.cs (100%) rename test/{Cuemon.Extensions.Integrity.Tests => Cuemon.Extensions.Data.Integrity.Tests}/Cuemon.Extensions.Data.Integrity.Tests.csproj (100%) rename test/{Cuemon.Extensions.Integrity.Tests => Cuemon.Extensions.Data.Integrity.Tests}/DateTimeExtensionsTest.cs (100%) diff --git a/Cuemon.sln b/Cuemon.sln index cb21f6df5..0afcf8198 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -55,7 +55,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Net.Tests", "test\Cuemon.Extensions.Net.Tests\Cuemon.Extensions.Net.Tests.csproj", "{E1C8F634-F655-487D-80B4-82F5F149FBA9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Data.Integrity.Tests", "test\Cuemon.Extensions.Integrity.Tests\Cuemon.Extensions.Data.Integrity.Tests.csproj", "{4B1D7CA2-67E0-4D54-BBCA-6607A7770E44}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Data.Integrity.Tests", "test\Cuemon.Extensions.Data.Integrity.Tests\Cuemon.Extensions.Data.Integrity.Tests.csproj", "{4B1D7CA2-67E0-4D54-BBCA-6607A7770E44}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Core.Tests", "test\Cuemon.Extensions.Core.Tests\Cuemon.Extensions.Core.Tests.csproj", "{6ACD0AEE-4B47-4B29-9EA8-4FF9C20FF2F6}" EndProject diff --git a/test/Cuemon.Extensions.Integrity.Tests/AssemblyExtensionsTest.cs b/test/Cuemon.Extensions.Data.Integrity.Tests/AssemblyExtensionsTest.cs similarity index 100% rename from test/Cuemon.Extensions.Integrity.Tests/AssemblyExtensionsTest.cs rename to test/Cuemon.Extensions.Data.Integrity.Tests/AssemblyExtensionsTest.cs diff --git a/test/Cuemon.Extensions.Integrity.Tests/Cuemon.Extensions.Data.Integrity.Tests.csproj b/test/Cuemon.Extensions.Data.Integrity.Tests/Cuemon.Extensions.Data.Integrity.Tests.csproj similarity index 100% rename from test/Cuemon.Extensions.Integrity.Tests/Cuemon.Extensions.Data.Integrity.Tests.csproj rename to test/Cuemon.Extensions.Data.Integrity.Tests/Cuemon.Extensions.Data.Integrity.Tests.csproj diff --git a/test/Cuemon.Extensions.Integrity.Tests/DateTimeExtensionsTest.cs b/test/Cuemon.Extensions.Data.Integrity.Tests/DateTimeExtensionsTest.cs similarity index 100% rename from test/Cuemon.Extensions.Integrity.Tests/DateTimeExtensionsTest.cs rename to test/Cuemon.Extensions.Data.Integrity.Tests/DateTimeExtensionsTest.cs From 1923487858b3af6ade7c270e5b8dd4684a1b55a2 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Sun, 30 Aug 2020 16:05:50 +0200 Subject: [PATCH 069/385] Update azure-pipelines.yml for Azure Pipelines Updated with build on both Windows and Linux (due to minor variants). Also, full code coverage on SonarCloud as well as Azure DevOps. --- azure-pipelines.yml | 90 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 16 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 17b153700..9c31916ba 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -2,16 +2,25 @@ trigger: - development variables: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: 1 BuildSource: 'src' BuildPlatform: 'Any CPU' BuildConfiguration: 'Release' jobs: - job: CI - timeoutInMinutes: 360 + timeoutInMinutes: 120 + + strategy: + matrix: + Linux_Build_and_Test: + imageName: 'ubuntu-20.04' + Windows_Build_and_Test: + imageName: 'windows-2019' pool: - vmImage: 'windows-2019' + vmImage: $(imageName) steps: - task: UseDotNet@2 @@ -31,6 +40,14 @@ jobs: custom: tool arguments: 'install --global nbgv' + - task: DotNetCoreCLI@2 + condition: eq(variables['Agent.OS'], 'Linux') + displayName: 'Install ReportGenerator tool' + inputs: + command: custom + custom: tool + arguments: install --global dotnet-reportgenerator-globaltool + - script: 'nbgv cloud' displayName: 'Set Version using NBGV' @@ -50,9 +67,10 @@ jobs: displayName: Restore inputs: command: restore - projects: '**/*.csproj' + projects: **/*.csproj - task: SonarCloudPrepare@1 + condition: eq(variables['Agent.OS'], 'Linux') displayName: 'Prepare Analysis on SonarCloud' inputs: SonarCloud: 'Cuemon-SonarCloud' @@ -61,32 +79,72 @@ jobs: projectKey: 'Cuemon' projectName: 'Cuemon' projectVersion: '$(GitAssemblyInformationalVersion)' + extraProperties: | + sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/**/*opencover.xml + sonar.cs.vstest.reportsPaths=$(Agent.TempDirectory)/*.trx + + - task: DotNetCoreCLI@2 + displayName: 'Build netcoreapp3.0 compatible Assemblies' + inputs: + command: 'build' + projects: | + src/**/Cuemon.AspNetCore*.csproj + src/**/Cuemon.Extensions.AspNetCore*.csproj + src/**/Cuemon.Extensions.Xunit.csproj + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)/netcoreapp3.0 --framework netcoreapp3.0' + workingDirectory: '$(BuildSource)' + + - task: DotNetCoreCLI@2 + displayName: 'Build netstandard2.1 compatible Assemblies' + inputs: + command: 'build' + projects: | + src/**/Cuemon.Extensions.IO.csproj + src/**/Cuemon.IO.csproj + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)/netstandard2.1 --framework netstandard2.1' + workingDirectory: '$(BuildSource)' - task: DotNetCoreCLI@2 - displayName: 'Build and Pack Assemblies' + displayName: 'Build netstandard2.0 compatible Assemblies' inputs: - command: pack - packagesToPack: 'src/**/*.csproj' - configuration: '$(BuildConfiguration)' - packDirectory: '$(Build.ArtifactStagingDirectory)/artifacts' - verbosityPack: Minimal + command: 'build' + projects: src/**/*.csproj + arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)/netstandard2.0 --framework netstandard2.0' + workingDirectory: '$(BuildSource)' - task: DotNetCoreCLI@2 - displayName: Test + displayName: 'Test Solution' inputs: - command: test - projects: 'test/**/*.csproj' - arguments: '--configuration $(BuildConfiguration) --collect "Code coverage"' + command: 'test' + projects: test/**/*.csproj + arguments: '--configuration $(BuildConfiguration) /p:CollectCoverage=true /p:CoverletOutputFormat=opencover' publishTestResults: true + - script: reportgenerator "-reports:**/*.opencover.xml" "-targetdir:$(Build.SourcesDirectory)/Coverage" "-reporttypes:Cobertura;HTMLInline;HTMLChart" + condition: eq(variables['Agent.OS'], 'Linux') + displayName: 'Create Code Coverage Reports' + + - task: PublishCodeCoverageResults@1 + condition: eq(variables['Agent.OS'], 'Linux') + displayName: 'Publish Code Coverage' + inputs: + codeCoverageTool: Cobertura + summaryFileLocation: '$(Build.SourcesDirectory)/Coverage/Cobertura.xml' + reportDirectory: '$(Build.SourcesDirectory)/Coverage' + - task: SonarCloudAnalyze@1 + condition: eq(variables['Agent.OS'], 'Linux') displayName: 'Run Sonar Cloud Code Analysis' - task: SonarCloudPublish@1 + condition: eq(variables['Agent.OS'], 'Linux') displayName: 'Publish Quality Gate Result to Sonar Cloud' inputs: pollingTimeoutSec: '300' - - publish: $(Build.ArtifactStagingDirectory)\artifacts - displayName: 'Publish Artifacts (Nuget)' - artifact: BuildPackages \ No newline at end of file + - task: PublishBuildArtifacts@1 + displayName: 'Publish compiled DLLs' + inputs: + PathtoPublish: $(Build.ArtifactStagingDirectory) + ArtifactName: Packages + publishLocation: Container \ No newline at end of file From 77e450cf0ffb8b1e035616278dd42e6d2c816924 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Sun, 30 Aug 2020 16:06:49 +0200 Subject: [PATCH 070/385] Update azure-pipelines.yml for Azure Pipelines Updated with build on both Windows and Linux (due to minor variants). Also, full code coverage on SonarCloud as well as Azure DevOps. --- azure-pipelines.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9c31916ba..6b43e545c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -67,7 +67,8 @@ jobs: displayName: Restore inputs: command: restore - projects: **/*.csproj + projects: | + **/*.csproj - task: SonarCloudPrepare@1 condition: eq(variables['Agent.OS'], 'Linux') From 74931ad1e3b24ed3f2549b08f01cfd6f18c83be9 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Sun, 30 Aug 2020 16:16:04 +0200 Subject: [PATCH 071/385] Update azure-pipelines.yml for Azure Pipelines Added codecov.io integration. --- azure-pipelines.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6b43e545c..46ded749f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -133,6 +133,10 @@ jobs: summaryFileLocation: '$(Build.SourcesDirectory)/Coverage/Cobertura.xml' reportDirectory: '$(Build.SourcesDirectory)/Coverage' + - bash: bash <(curl -s https://codecov.io/bash) + condition: eq(variables['Agent.OS'], 'Linux') + displayName: 'Upload to codecov.io' + - task: SonarCloudAnalyze@1 condition: eq(variables['Agent.OS'], 'Linux') displayName: 'Run Sonar Cloud Code Analysis' From 126850550a799c7d78d93edf85442fe7070b7f22 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 16:18:44 +0200 Subject: [PATCH 072/385] Removed coverlet.collector. --- Directory.Build.props | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 413d13217..382830614 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -61,10 +61,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - From fe467959b0d98d4594342cfa79c165cee1b34565 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 17:01:09 +0200 Subject: [PATCH 073/385] SourceLink changes. --- Directory.Build.props | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 382830614..9bfb37144 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -21,7 +21,7 @@ https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png https://www.cuemon.net/ MIT - https://github.com/gimlichael/CuemonCore + https://github.com/gimlichael/Cuemon git en-US true @@ -36,12 +36,15 @@ - + + all + runtime; build; native; contentfiles; analyzers + - - + + From 29716ccca95a790d21cfb79399407ccc2d7d1b89 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 17:26:25 +0200 Subject: [PATCH 074/385] Removed Microsoft.SourceLink.GitHub integration due to bug with coverlet. More info: https://github.com/coverlet-coverage/coverlet/issues/940 --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9bfb37144..535930fd2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -36,10 +36,10 @@ - + From d63c457630d3a1af963e8ae0ed6c973eedc2cf4a Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 19:27:12 +0200 Subject: [PATCH 075/385] Updated because of another bug with coverlet (https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/KnownIssues.md#1-vstest-stops-process-execution-earlydotnet-test). --- Directory.Build.props | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Directory.Build.props b/Directory.Build.props index 535930fd2..0cea048b5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -64,6 +64,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From e3bdd95f25f771311ef81982cb08d5e93ac20e2d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 19:58:09 +0200 Subject: [PATCH 076/385] https://github.com/coverlet-coverage/coverlet#vstest-integration-preferred-due-to-known-issue-supports-only-net-core-application --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 46ded749f..3ede04e0f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -118,7 +118,7 @@ jobs: inputs: command: 'test' projects: test/**/*.csproj - arguments: '--configuration $(BuildConfiguration) /p:CollectCoverage=true /p:CoverletOutputFormat=opencover' + arguments: '--configuration $(BuildConfiguration) --collect:"XPlat Code Coverage" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover' publishTestResults: true - script: reportgenerator "-reports:**/*.opencover.xml" "-targetdir:$(Build.SourcesDirectory)/Coverage" "-reporttypes:Cobertura;HTMLInline;HTMLChart" From 0e7b1694df57ed949bb2c00fd603293ed8a08df0 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 20:18:41 +0200 Subject: [PATCH 077/385] Added Jittter. --- .../TransientOperationTest.cs | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/test/Cuemon.Resilience.Tests/TransientOperationTest.cs b/test/Cuemon.Resilience.Tests/TransientOperationTest.cs index 37325ae9a..ce90354d2 100644 --- a/test/Cuemon.Resilience.Tests/TransientOperationTest.cs +++ b/test/Cuemon.Resilience.Tests/TransientOperationTest.cs @@ -18,6 +18,7 @@ public class TransientOperationTest : Test private const string ExpectedResult = "OK"; private const int ExpectedRetryAttempts = 2; + private static readonly TimeSpan Jitter = TimeSpan.FromMilliseconds(1000); private const int NormalRunIncrement = 1; private const int DescriptiveExceptionCauseIncrement = 1; private static readonly TimeSpan ExpectedRecoveryWaitTime = TimeSpan.FromSeconds(1); @@ -78,7 +79,7 @@ public void WithFunc_ShouldTriggerRetryAndSucceed() var profiler = TimeMeasure.WithFunc(() => TransientOperation.WithFunc(FuncTransientOperation.FailUntilExpectedRetryAttemptsIsReached, id, ExpectedRetryAttempts, _retryTracker, TransientOperationOptionsCallback)); Assert.Equal(ExpectedResult, profiler.Result); - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); } @@ -96,7 +97,7 @@ public void WithFunc_ShouldTriggerTransientFaultException() }); var tfe = _transientFaultTracker.Single(pair => pair.Key == id).Value; - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)tfe.TotalRecoveryWaitTime.TotalSeconds); Assert.Equal((int)ExpectedRecoveryWaitTime.TotalSeconds, (int)tfe.RecoveryWaitTime.TotalSeconds); @@ -136,7 +137,7 @@ public void WithFunc_ShouldTriggerInvalidOperationException() TestOutput.WriteLine(aex.ToString()); }); - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); } @@ -160,7 +161,7 @@ public void WithAction_ShouldTriggerRetryAndSucceed() var profiler = TimeMeasure.WithAction(() => TransientOperation.WithAction(ActionTransientOperation.FailUntilExpectedRetryAttemptsIsReached, id, ExpectedRetryAttempts, _retryTracker, TransientOperationOptionsCallback)); - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); } @@ -178,7 +179,7 @@ public void WithAction_ShouldTriggerTransientFaultException() }); var tfe = _transientFaultTracker.Single(pair => pair.Key == id).Value; - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)tfe.TotalRecoveryWaitTime.TotalSeconds); Assert.Equal((int)ExpectedRecoveryWaitTime.TotalSeconds, (int)tfe.RecoveryWaitTime.TotalSeconds); @@ -218,7 +219,7 @@ public void WithAction_ShouldTriggerInvalidOperationException() TestOutput.WriteLine(aex.ToString()); }); - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); } @@ -242,7 +243,7 @@ public async Task WithActionAsync_ShouldTriggerRetryAndSucceed() var profiler = await TimeMeasure.WithActionAsync(ct => TransientOperation.WithActionAsync(AsyncActionTransientOperation.FailUntilExpectedRetryAttemptsIsReachedAsync, id, ExpectedRetryAttempts, _retryTracker, ct, TransientOperationOptionsCallback)); - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); } @@ -260,7 +261,7 @@ public async Task WithActionAsync_ShouldTriggerTransientFaultException() }); var tfe = _transientFaultTracker.Single(pair => pair.Key == id).Value; - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)tfe.TotalRecoveryWaitTime.TotalSeconds); Assert.Equal((int)ExpectedRecoveryWaitTime.TotalSeconds, (int)tfe.RecoveryWaitTime.TotalSeconds); @@ -300,7 +301,7 @@ public async Task WithActionAsync_ShouldTriggerInvalidOperationException() TestOutput.WriteLine(aex.ToString()); }); - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); } @@ -313,7 +314,7 @@ public async Task WithFuncAsync_ShouldTriggerRetryAndSucceedAsync() var profiler = await TimeMeasure.WithFuncAsync(ct => TransientOperation.WithFuncAsync(AsyncFuncTransientOperation.FailUntilExpectedRetryAttemptsIsReachedAsync, id, ExpectedRetryAttempts, _retryTracker, ct, TransientOperationOptionsCallback)); Assert.Equal(ExpectedResult, profiler.Result); - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); } @@ -331,7 +332,7 @@ public async Task WithFuncAsync_ShouldTriggerTransientFaultException() }); var tfe = _transientFaultTracker.Single(pair => pair.Key == id).Value; - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)tfe.TotalRecoveryWaitTime.TotalSeconds); Assert.Equal((int)ExpectedRecoveryWaitTime.TotalSeconds, (int)tfe.RecoveryWaitTime.TotalSeconds); @@ -371,7 +372,9 @@ public async Task WithFuncAsync_ShouldTriggerInvalidOperationException() TestOutput.WriteLine(aex.ToString()); }); - Assert.Equal((int)TimeSpan.FromSeconds(ExpectedRetryAttempts).TotalSeconds, (int)profiler.Elapsed.TotalSeconds); + TestOutput.WriteLine($"Profiler: {profiler.Elapsed.TotalSeconds} seconds."); + + Assert.InRange(profiler.Elapsed.TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) - Jitter).TotalSeconds, (TimeSpan.FromSeconds(ExpectedRetryAttempts) + Jitter).TotalSeconds); Assert.Equal(ExpectedRetryAttempts, _retryTracker[id]); } } From ac0d001f3e53691c8a4d2b004b6855d165034875 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 20:31:37 +0200 Subject: [PATCH 078/385] Added codegov.io badge. --- README.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fe8df5a2c..7d25a90ec 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,27 @@ The release for now is planned to be 6.0.0. [![Build Status](https://dev.azure.com/gimlichael/Cuemon/_apis/build/status/gimlichael.Cuemon?branchName=development)](https://dev.azure.com/gimlichael/Cuemon/_build/latest?definitionId=9&branchName=development) -[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=bugs)](https://sonarcloud.io/dashboard?id=Cuemon) -[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=code_smells)](https://sonarcloud.io/dashboard?id=Cuemon) +[![codecov](https://codecov.io/gh/gimlichael/Cuemon/branch/development/graph/badge.svg)](https://codecov.io/gh/gimlichael/Cuemon) + [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=coverage)](https://sonarcloud.io/dashboard?id=Cuemon) -[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=ncloc)](https://sonarcloud.io/dashboard?id=Cuemon) + +[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=Cuemon)](https://sonarcloud.io/dashboard?id=Cuemon) + [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=Cuemon) + [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=Cuemon) + [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=security_rating)](https://sonarcloud.io/dashboard?id=Cuemon) + +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=ncloc)](https://sonarcloud.io/dashboard?id=Cuemon) + +[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=code_smells)](https://sonarcloud.io/dashboard?id=Cuemon) + [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_index)](https://sonarcloud.io/dashboard?id=Cuemon) -[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=Cuemon) -[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=Cuemon)](https://sonarcloud.io/dashboard?id=Cuemon) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=bugs)](https://sonarcloud.io/dashboard?id=Cuemon) + +[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=Cuemon) Stay tuned! From 91b7575de4f8aca5548d3d36b64f8c493e7594b2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 20:53:56 +0200 Subject: [PATCH 079/385] Should fix coverlet bug. https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/DeterministicBuild.md --- Directory.Build.targets | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Directory.Build.targets diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 000000000..14fbb7992 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,20 @@ + + + $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)')) + + + + + + + + + + + <_LocalTopLevelSourceRoot Include="@(SourceRoot)" Condition="'%(SourceRoot.NestedRoot)' == ''"/> + + + \ No newline at end of file From 40b45576be6f77f10c3b86eba317fac4017695d7 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 20:59:30 +0200 Subject: [PATCH 080/385] Added "jitter". --- test/Cuemon.Resilience.Tests/TransientOperationTest.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/Cuemon.Resilience.Tests/TransientOperationTest.cs b/test/Cuemon.Resilience.Tests/TransientOperationTest.cs index ce90354d2..51492acb3 100644 --- a/test/Cuemon.Resilience.Tests/TransientOperationTest.cs +++ b/test/Cuemon.Resilience.Tests/TransientOperationTest.cs @@ -279,8 +279,13 @@ public async Task WithActionAsync_ShouldTriggerLatencyException() var profiler = await TimeMeasure.WithActionAsync(async ct => { var aex = await Assert.ThrowsAsync(() => TransientOperation.WithActionAsync(AsyncActionTransientOperation.TriggerLatencyExceptionAsync, id, _retryTracker, ct, TransientOperationOptionsCallback)); + + TestOutput.WriteLine(aex.ToString()); + Assert.IsType(aex.InnerExceptions.First()); - Assert.Equal(NormalRunIncrement + DescriptiveExceptionCauseIncrement, aex.InnerExceptions.Count); + + var low = NormalRunIncrement + DescriptiveExceptionCauseIncrement; + Assert.InRange(aex.InnerExceptions.Count, low, low + 1); // expect 2 - allow 3 in rare cases TestOutput.WriteLine(aex.ToString()); }); From 7d41c34794d4c30eeff57cfcfcdc1d0fc2addaa6 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 30 Aug 2020 21:00:58 +0200 Subject: [PATCH 081/385] Re-enabled SourceLink due to coverlet fix. --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 0cea048b5..169d9f1d1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -36,10 +36,10 @@ - + From ea3201332927d55ddc0e6ddaf9774ab5fef158ca Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 31 Aug 2020 01:29:48 +0200 Subject: [PATCH 082/385] Preparation steps for preview release of Cuemon. --- azure-pipelines.yml | 20 +++++++++++++------ .../Cuemon.AspNetCore.Authentication.csproj | 4 ++-- .../Cuemon.AspNetCore.Mvc.csproj | 4 ++-- .../Cuemon.AspNetCore.Razor.csproj | 4 ++-- .../Cuemon.AspNetCore.csproj | 4 ++-- src/Cuemon.Core/Cuemon.Core.csproj | 3 ++- .../Cuemon.Data.Integrity.csproj | 2 +- .../Cuemon.Data.SqlClient.csproj | 2 +- src/Cuemon.Data/Cuemon.Data.csproj | 2 +- .../Cuemon.Diagnostics.csproj | 2 +- ...Core.Mvc.Formatters.Newtonsoft.Json.csproj | 4 ++-- ...sions.AspNetCore.Mvc.Formatters.Xml.csproj | 4 ++-- .../Cuemon.Extensions.AspNetCore.Mvc.csproj | 4 ++-- .../Cuemon.Extensions.AspNetCore.csproj | 4 ++-- ...emon.Extensions.Collections.Generic.csproj | 4 ++-- ....Extensions.Collections.Specialized.csproj | 4 ++-- .../Cuemon.Extensions.Core.csproj | 4 ++-- .../Cuemon.Extensions.Data.Integrity.csproj | 2 +- .../Cuemon.Extensions.Data.csproj | 2 +- ...emon.Extensions.DependencyInjection.csproj | 2 +- .../Cuemon.Extensions.Diagnostics.csproj | 2 +- .../Cuemon.Extensions.IO.csproj | 4 ++-- .../Cuemon.Extensions.Net.csproj | 2 +- .../Cuemon.Extensions.Newtonsoft.Json.csproj | 2 +- .../Cuemon.Extensions.Reflection.csproj | 2 +- .../Cuemon.Extensions.Text.csproj | 2 +- .../Cuemon.Extensions.Threading.csproj | 2 +- .../Cuemon.Extensions.Xml.csproj | 4 ++-- src/Cuemon.Extensions.Xml/StringExtensions.cs | 2 -- .../Cuemon.Extensions.Xunit.csproj | 2 +- src/Cuemon.IO/Cuemon.IO.csproj | 2 +- src/Cuemon.Net/Cuemon.Net.csproj | 2 +- .../Cuemon.Resilience.csproj | 2 +- .../Cuemon.Runtime.Caching.csproj | 2 +- src/Cuemon.Threading/Cuemon.Threading.csproj | 2 +- src/Cuemon.Xml/Cuemon.Xml.csproj | 2 +- version.json | 2 +- 37 files changed, 63 insertions(+), 56 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3ede04e0f..3e0f5c422 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -10,13 +10,13 @@ variables: jobs: - job: CI - timeoutInMinutes: 120 + timeoutInMinutes: 75 strategy: matrix: Linux_Build_and_Test: imageName: 'ubuntu-20.04' - Windows_Build_and_Test: + Windows_Build_Test_and_Package: imageName: 'windows-2019' pool: @@ -29,7 +29,7 @@ jobs: version: 2.2.207 - task: UseDotNet@2 - displayName: 'Use .Net Core 3.1.401' + displayName: 'Use .Net Core SDK 3.1.401' inputs: version: 3.1.401 @@ -92,7 +92,7 @@ jobs: src/**/Cuemon.AspNetCore*.csproj src/**/Cuemon.Extensions.AspNetCore*.csproj src/**/Cuemon.Extensions.Xunit.csproj - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)/netcoreapp3.0 --framework netcoreapp3.0' + arguments: '--configuration $(BuildConfiguration) --no-restore --framework netcoreapp3.0' workingDirectory: '$(BuildSource)' - task: DotNetCoreCLI@2 @@ -102,7 +102,7 @@ jobs: projects: | src/**/Cuemon.Extensions.IO.csproj src/**/Cuemon.IO.csproj - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)/netstandard2.1 --framework netstandard2.1' + arguments: '--configuration $(BuildConfiguration) --no-restore --framework netstandard2.1' workingDirectory: '$(BuildSource)' - task: DotNetCoreCLI@2 @@ -110,7 +110,7 @@ jobs: inputs: command: 'build' projects: src/**/*.csproj - arguments: '--configuration $(BuildConfiguration) --no-restore --output $(Build.ArtifactStagingDirectory)/netstandard2.0 --framework netstandard2.0' + arguments: '--configuration $(BuildConfiguration) --no-restore --framework netstandard2.0' workingDirectory: '$(BuildSource)' - task: DotNetCoreCLI@2 @@ -147,6 +147,14 @@ jobs: inputs: pollingTimeoutSec: '300' + - task: DotNetCoreCLI@2 + condition: eq( variables['Agent.OS'], 'Windows_NT' ) + displayName: dotnet pack + inputs: + command: pack + packagesToPack: src/**/*.csproj + nobuild: true + - task: PublishBuildArtifacts@1 displayName: 'Publish compiled DLLs' inputs: diff --git a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj index d75a43e2b..e1dc8a589 100644 --- a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj +++ b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj @@ -8,8 +8,8 @@ Cuemon.AspNetCore.Authentication Cuemon.AspNetCore.Authentication - The Cuemon.AspNetCore.Authentication assembly provides supplemental ways of authentication forms to Microsoft ASP.NET Core. - + The Cuemon.AspNetCore.Authentication namespace contains implementations of authentication forms and features related to the Cuemon.AspNetCore namespace. + basic-authentication digest-access-authentication hmac-authentication diff --git a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj index de8a5c9a7..0764b0e9d 100644 --- a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj +++ b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj @@ -8,8 +8,8 @@ Cuemon.AspNetCore.Mvc Cuemon.AspNetCore.Mvc - The Cuemon.AspNetCore.Mvc assembly is a fit companion to the Microsoft.AspNetCore.Mvc namespace that provides an abundant range of filters and action results to your codebelt. - + The Cuemon.AspNetCore.Mvc namespace contains an abundant range of filters, action results and other features related to the Microsoft.AspNetCore.Mvc namespace. + cacheable-object-factory cacheable-object-result content-based-object-result content-time-based-object-result see-other-result http-cacheable-filter http-entity-tag-header-filter http-last-modified-header-filter fault-descriptor-filter fault-resolver http-request-evidence time-measuring-filter configurable-action-filter diff --git a/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj b/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj index 6161d5a53..63d4d46da 100644 --- a/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj +++ b/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj @@ -8,8 +8,8 @@ Cuemon.AspNetCore.Razor Cuemon.AspNetCore.Razor - The Cuemon.AspNetCore.Razor assembly is a supplement to Microsoft ASP.NET Core. - + The Cuemon.AspNetCore.Razor namespace contains features related to the Microsoft.AspNetCore.Razor namespace. + cdn-tag-helper cdn-uri-scheme image-cdn-tag-helper link-cdn-tag-helper script-cdn-tag-helper diff --git a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj index 2fa2d4eae..d83428ee6 100644 --- a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj +++ b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj @@ -8,8 +8,8 @@ Cuemon.AspNetCore Cuemon.AspNetCore - The Cuemon.AspNetCore assembly is a supplement to Microsoft ASP.NET Core. - + The Cuemon.AspNetCore namespace contains abundant features related to the Microsoft.AspNetCore namespace. + configurable-middleware middleware user-agent-sentinel throttling hosting-environment cache-busting application-builder-factory diff --git a/src/Cuemon.Core/Cuemon.Core.csproj b/src/Cuemon.Core/Cuemon.Core.csproj index 01301b8d6..b473a931d 100644 --- a/src/Cuemon.Core/Cuemon.Core.csproj +++ b/src/Cuemon.Core/Cuemon.Core.csproj @@ -9,7 +9,8 @@ Cuemon Cuemon.Core Cuemon - The Cuemon.Core assembly is the patriarch of the Cuemon family and provides fundamental-, utility- and base-classes that define commonly-used value and reference data types, events and event handlers, interfaces, attribute, and feature rich delegates to greatly support functional programming. A great addition to the well established System namespace to make your daily operations easier to work with. + The Cuemon namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. + action-factory bit-unit byte-unit calculator configure configure-revert configure-exchange configurable condition options-pattern data-reader decorator delimited-string disposable finalize-disposable safe-invoke safe-invoke-async func-factory patterns reference-project clean-architecture clean-code task-action-factory task-func-factory template template-factory time-range time-unit validator guard text-encoding parser-factory security aes-cryptor cyclic-redundancy-check fowler-noll-vo-hash hash-factory hash-result hmac-message-digest hmac-secure-hash-algorithm keyed-crypto-hash keyed-crypto-algorithm message-digest non-crypto-algorithm secure-hash-algorithm unkeyed-crypto-hash diff --git a/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj b/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj index 2a93f0b3d..3ff8b0d5f 100644 --- a/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj +++ b/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj @@ -8,7 +8,7 @@ Cuemon.Data.Integrity Cuemon.Data.Integrity - The Cuemon.Data.Integrity assembly provides access to data integrity related operations. + The Cuemon.Data.Integrity namespace contains classes that provide functionality to help insure integrity of data-centric operations. cache-validator checksum data-integrity diff --git a/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj b/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj index 841e11142..bd571a6ca 100644 --- a/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj +++ b/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj @@ -8,7 +8,7 @@ Cuemon.Data.SqlClient Cuemon.Data.SqlClient - The Cuemon.Data.SqlClient assembly provides different Microsoft SQL Server implementations of different abstractions found in the Cuemon.Data namespace. + The Cuemon.Data.SqlClient namespace contains different Microsoft SQL Server implementations of different abstractions found in the Cuemon.Data namespace. sql sql-in-operator sql-query-builder sql-data-manager transient-fault-handling diff --git a/src/Cuemon.Data/Cuemon.Data.csproj b/src/Cuemon.Data/Cuemon.Data.csproj index 67ba7182d..744ca737b 100644 --- a/src/Cuemon.Data/Cuemon.Data.csproj +++ b/src/Cuemon.Data/Cuemon.Data.csproj @@ -8,7 +8,7 @@ Cuemon.Data Cuemon.Data - The Cuemon.Data assembly provides access to abstractions related to the System.Data namespace. + The Cuemon.Data namespace contains abstractions related to the System.Data namespace. database db abstractions dto data-transfer row column bulk-copy diff --git a/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj b/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj index 0435a44c0..7277cb46a 100644 --- a/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj +++ b/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj @@ -8,7 +8,7 @@ Cuemon.Diagnostics Cuemon.Diagnostics - The Cuemon.Diagnostics assembly provides access to features that extends the System.Diagnostics namespace. + The Cuemon.Diagnostics namespace contains features that extends the System.Diagnostics namespace. time-measuring async-time-measuring profiler exception-descriptor diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj index 564002462..5dbe4fddb 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj @@ -9,8 +9,8 @@ Cuemon Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json - The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json assembly provides extension methods and JSON formatters for ASP.NET Core MVC that uses the Newtonsoft.Json package. - extension-methods extensions + The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json namespace contains extension methods and JSON formatters for ASP.NET Core MVC that uses the Newtonsoft.Json Nuget package. + extension-methods extensions json-converters diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj index 6f302dc69..b79b1d899 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj @@ -8,8 +8,8 @@ Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml - The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml assembly provides extension methods and XML formatters for ASP.NET Core MVC. - extension-methods extensions + The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml namespace provides extension methods and XML formatters for ASP.NET Core MVC. + extension-methods extensions xml-converters diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj index 0104c80fb..62cb73a30 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj @@ -8,8 +8,8 @@ Cuemon.Extensions.AspNetCore.Mvc Cuemon.Extensions.AspNetCore.Mvc - The Cuemon.Extensions.AspNetCore.Mvc assembly provides extension methods and general improvement to the Cuemon.AspNetCore assembly. - extension-methods extensions + The Cuemon.Extensions.AspNetCore.Mvc namespace contains extension methods and features related to the Cuemon.AspNetCore.Mvc namespace. + extension-methods extensions assembly-cache-busting cache-busting dynamic-cache-busting use-when make-cacheable diff --git a/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj b/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj index 542102cfa..1b60f8643 100644 --- a/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj +++ b/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj @@ -8,8 +8,8 @@ Cuemon.Extensions.AspNetCore Cuemon.Extensions.AspNetCore - The Cuemon.Extensions.AspNetCore assembly provides extension methods and general improvement to the Cuemon.AspNetCore assembly. - extension-methods extensions + The Cuemon.Extensions.AspNetCore namespace contains extension methods and features related to the Cuemon.AspNetCore namespace. + extension-methods extensions memory-throttling-cache use-hosting-environment-header use-correlation-identifier-header use-request-identifier-header use-user-agent-sentinel use-custom-throttling-sentinel diff --git a/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj b/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj index 1ec8efa93..bc1b93677 100644 --- a/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj +++ b/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj @@ -8,8 +8,8 @@ Cuemon.Extensions.Collections.Generic Cuemon.Extensions.Collections.Generic - The Cuemon.Extensions.Collections.Generic assembly provides access to extension methods that supports the System.Collections.Generic namespace. - extension-methods extensions + The Cuemon.Extensions.Collections.Generic namespace contains extension methods and features related to the System.Collections.Generic namespace. + extension-methods extensions to-partitioner chunk shuffle random-or-default yield diff --git a/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj b/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj index 6b8df096d..2459267b6 100644 --- a/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj +++ b/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj @@ -8,8 +8,8 @@ Cuemon.Extensions.Collections.Specialized Cuemon.Extensions.Collections.Specialized - The Cuemon.Extensions.Collections.Specialized assembly provides access to extension methods that supports the Cuemon.Collections.Specialized namespace. - extension-methods extensions + The Cuemon.Extensions.Collections.Specialized namespace contains extension methods and features related to the Cuemon.Collections.Specialized namespace. + extension-methods extensions to-name-value-collection to-dictionary diff --git a/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj b/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj index 8474d95a4..65bd033cd 100644 --- a/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj +++ b/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj @@ -9,8 +9,8 @@ Cuemon.Extensions Cuemon.Extensions.Core Cuemon.Extensions - The Cuemon.Extensions.Core assembly provides access to extension methods that supports the Cuemon namespace. - extension-methods extensions core + The Cuemon.Extensions namespace contains extension methods and features related to the Cuemon namespace. + extension-methods extensions to-byte-array flatten to-encoded-string configure create-instance action-delegate options-pattern diff --git a/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj b/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj index 5aa829a1c..1dc479996 100644 --- a/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj +++ b/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Data.Integrity Cuemon.Extensions.Data.Integrity - The Cuemon.Extensions.Data.Integrity assembly provides access to extension methods that supports the Cuemon.Data.Integrity namespace. + The Cuemon.Extensions.Data.Integrity namespace contains extension methods and features related to the Cuemon.Data.Integrity namespace. extension-methods extensions get-cache-validator combine-with diff --git a/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj b/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj index b5f9258c6..4cf38741f 100644 --- a/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj +++ b/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj @@ -9,7 +9,7 @@ Cuemon Cuemon.Extensions.Data Cuemon.Extensions.Data - The Cuemon.Extensions.Data assembly provides access to extension methods that supports the Cuemon.Data namespace. + The Cuemon.Extensions.Data namespace contains extension methods and features related to the Cuemon.Data namespace. extension-methods extensions to-rows to-columns embed diff --git a/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj b/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj index 48ab3709c..68c15e73b 100644 --- a/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj +++ b/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.DependencyInjection Cuemon.Extensions.DependencyInjection - The Cuemon.Extensions.DependencyInjection assembly provides access to extension methods that supports the Microsoft.Extensions.DependencyInjection namespace. + The Cuemon.Extensions.DependencyInjection namespace contains extension methods and features related to the Microsoft.Extensions.DependencyInjection namespace. extension-methods extensions add tryadd diff --git a/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj b/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj index d8bccdad7..e80b55256 100644 --- a/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj +++ b/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Diagnostics Cuemon.Extensions.Diagnostics - The Cuemon.Extensions.Diagnostics assembly provides access to extension methods that supports the Cuemon.Diagnostics namespace. + The Cuemon.Extensions.Diagnostics namespace contains extension methods and features related to the Cuemon.Diagnostics namespace. extension-methods extensions to-insights-string to-product-version to-file-version diff --git a/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj b/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj index 8294119bd..d95280d24 100644 --- a/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj +++ b/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj @@ -8,8 +8,8 @@ Cuemon.Extensions.IO Cuemon.Extensions.IO - The Cuemon.Extensions.IO assembly provides access to extension methods that supports the System.IO namespace. - extension-methods extensions to-byte-array to-byte-array-async write-async to-encoded-string to-encoded-string-async compress-brotli compress-brotli-async compress-deflate compress-deflate-async compress-gzip compress-gzip-async + The Cuemon.Extensions.IO namespace contains extension methods and features related to the System.IO namespace. + extension-methods extensions concat to-byte-array to-byte-array-async write-async to-encoded-string to-encoded-string-async compress-brotli compress-brotli-async compress-deflate compress-deflate-async compress-gzip compress-gzip-async diff --git a/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj b/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj index 3b551bb09..feaa27a5a 100644 --- a/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj +++ b/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Net Cuemon.Extensions.Net - The Cuemon.Extensions.Net assembly provides extension methods (query-string parsing, encoding, decoding, security and http communication) and features related to the System.Net namespace. A versatile HttpManager that transparently promotes the HttpClient is included. + The Cuemon.Extensions.Net namespace contains extension methods (query-string parsing, encoding, decoding, security and http communication) and features related to the System.Net namespace. A versatile HttpManager that transparently promotes the HttpClient is included. extension-methods extensions to-signed-uri validate-signed-uri http-manager-factory slim-http-client-factory i-http-client-factory diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj index 9d289ec9d..51739f243 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Newtonsoft.Json Cuemon.Extensions.Newtonsoft.Json - The Cuemon.Extensions.Newtonsoft.Json assembly provides extension methods and general improvements that supports the Newtonsoft.Json namespace. + The Cuemon.Extensions.Newtonsoft.Json namespace contains extension methods and features related to the Newtonsoft.Json namespace. extension-methods extensions jdata jdata-result json-converter json-formatter diff --git a/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj b/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj index cd4bba0db..373eb6ae3 100644 --- a/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj +++ b/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj @@ -9,7 +9,7 @@ Cuemon .NET Standard Cuemon.Extensions.Reflection Cuemon.Extensions.Reflection - The Cuemon.Extensions.Reflection assembly provides extension methods to the System.Reflection namespace. + The Cuemon.Extensions.Reflection namespace contains extension methods and features related to the System.Reflection namespace. extension-methods extensions get-assembly-version get-file-version get-product-version is-debug-build has-attributes is-auto-property get-runtime-properties-except-of diff --git a/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj b/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj index d3600fddc..96c956dd0 100644 --- a/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj +++ b/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Text Cuemon.Extensions.Text - The Cuemon.Extensions.Text assembly provides access to extension methods for the Cuemon.Text namespace. + The Cuemon.Extensions.Text namespace contains extension methods and features related to the Cuemon.Text namespace. extension-methods extensions to-encoded-string to-ascii-encoded-string diff --git a/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj b/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj index d23ead4ad..37edbeeb2 100644 --- a/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj +++ b/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Threading Cuemon.Extensions.Threading - The Cuemon.Extensions.Threading assembly provides access to extension methods for the System.Threading namespace. + The Cuemon.Extensions.Threading namespace contains extension methods and features related to the System.Threading namespace. extension-methods extensions continue-with-captured-context continue-with-suppressed-context diff --git a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj index 36de71b95..92ec641c1 100644 --- a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj +++ b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj @@ -9,8 +9,8 @@ Cuemon Cuemon.Extensions.Xml Cuemon.Extensions.Xml - The Cuemon.Extensions.Xml assembly provides access to extension methods for XML scenarios such as escaping, conversions, parsing, sanitizing, serialization and deserialization. - extension-methods extensions + The Cuemon.Extensions.Xml namespace contains extension methods and features related to the System.Xml namespace that provides access to XML scenarios such as escaping, conversions, parsing, sanitizing, serialization and deserialization. + extension-methods extensions to-xml-reader copy-xml-stream try-detect-xml-encoding chunk to-stream write-object diff --git a/src/Cuemon.Extensions.Xml/StringExtensions.cs b/src/Cuemon.Extensions.Xml/StringExtensions.cs index 0ff3aa3be..2cea96ee2 100644 --- a/src/Cuemon.Extensions.Xml/StringExtensions.cs +++ b/src/Cuemon.Extensions.Xml/StringExtensions.cs @@ -8,8 +8,6 @@ namespace Cuemon.Extensions.Xml /// public static class StringExtensions { - private static readonly string[][] EscapeStringPairs = new[] { new[] { "<", ">", """, "'", "&" }, new[] {"<", ">", "\"", "'", "&"} }; - /// /// Escapes the given XML . /// diff --git a/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj b/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj index 5e76b7c76..174fd4f75 100644 --- a/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj +++ b/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Xunit Cuemon.Extensions.Xunit - The Cuemon.Extensions.Xunit assembly provides access to extension methods and abstractions that supports the Xunit.Abstractions namespace. + The Cuemon.Extensions.Xunit namespace contains extension methods and features related to the Xunit.Abstractions namespace. test host-test diff --git a/src/Cuemon.IO/Cuemon.IO.csproj b/src/Cuemon.IO/Cuemon.IO.csproj index ea8936de7..b33752526 100644 --- a/src/Cuemon.IO/Cuemon.IO.csproj +++ b/src/Cuemon.IO/Cuemon.IO.csproj @@ -8,7 +8,7 @@ Cuemon.IO Cuemon.IO - The Cuemon.IO assembly provides access to features that extends the System.IO namespace through IDecorator extension methods. + The Cuemon.IO namespace provides access to features that extends the System.IO namespace through IDecorator extension methods. textreader textwriter brotli gzip deflate async diff --git a/src/Cuemon.Net/Cuemon.Net.csproj b/src/Cuemon.Net/Cuemon.Net.csproj index 871d90de9..ee835941a 100644 --- a/src/Cuemon.Net/Cuemon.Net.csproj +++ b/src/Cuemon.Net/Cuemon.Net.csproj @@ -8,7 +8,7 @@ Cuemon.Net Cuemon.Net - The Cuemon.Net assembly provides access to features that extends the System.Net namespace and includes a lightweight SMTP Client. + The Cuemon.Net namespace contains classes for HTTP communication, a lightweight SMTP Client while and other neat features related to the System.Net namespace. http-manager http-get http-post http-put http-patch http-delete http-trace mail-distributor smtp-client diff --git a/src/Cuemon.Resilience/Cuemon.Resilience.csproj b/src/Cuemon.Resilience/Cuemon.Resilience.csproj index 792681e01..790ab057c 100644 --- a/src/Cuemon.Resilience/Cuemon.Resilience.csproj +++ b/src/Cuemon.Resilience/Cuemon.Resilience.csproj @@ -8,7 +8,7 @@ Cuemon.Resilience Cuemon.Resilience - The Cuemon.Resilience assembly provides access to a lightweight resilience framework that support transient fault handling of operations. + The Cuemon.Resilience namespace contains a lightweight resilience framework that support transient fault handling of operations. transient-fault-evidence transient-fault-exception transient-operation async-transient-operation latency-exception diff --git a/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj b/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj index 8dfc44eb1..984e900bf 100644 --- a/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj +++ b/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj @@ -8,7 +8,7 @@ Cuemon.Runtime.Caching Cuemon.Runtime.Caching - The Cuemon.Runtime.Caching assembly provides access to features that extends the System.Runtime.Caching namespace. + The Cuemon.Runtime.Caching namespace contains features related to the System.Runtime.Caching namespace. caching-manager diff --git a/src/Cuemon.Threading/Cuemon.Threading.csproj b/src/Cuemon.Threading/Cuemon.Threading.csproj index f60bccbd7..29a6fb51b 100644 --- a/src/Cuemon.Threading/Cuemon.Threading.csproj +++ b/src/Cuemon.Threading/Cuemon.Threading.csproj @@ -8,7 +8,7 @@ Cuemon.Threading Cuemon.Threading - The Cuemon.Threading assembly provides access to features that extends the System.Threading namespace. + The Cuemon.Threading namespace contains features related to the System.Threading namespace. parallel-factory for-async for-each-async for-each-result-async for-result-async while-async while-result-async diff --git a/src/Cuemon.Xml/Cuemon.Xml.csproj b/src/Cuemon.Xml/Cuemon.Xml.csproj index 4a31fc077..0a2c58103 100644 --- a/src/Cuemon.Xml/Cuemon.Xml.csproj +++ b/src/Cuemon.Xml/Cuemon.Xml.csproj @@ -8,7 +8,7 @@ Cuemon.Xml Cuemon.Xml - The Cuemon.Xml assembly provides access to features that extends both the System.Xml- and System.Xml.Serialization namespaces. Included is a lightweight XML serializer framework that offers the same flexibility provided by the JSON equivalent from Newtonsoft. + The Cuemon.Xml namespace contains features related to both the System.Xml- and System.Xml.Serialization namespaces. Included is a lightweight XML serializer framework that offers the same flexibility provided by the JSON equivalent from Newtonsoft. xml-formatter xml-converter xml-serializer xml-factories diff --git a/version.json b/version.json index 8b3d9a59c..9627c5d24 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "6.0.0-prerelease.{height}", + "version": "6.0.0-preview.{height}", "assemblyVersion": { "precision": "minor" From cb286a043217856459d7b51dbc305edb5627227f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 31 Aug 2020 01:49:12 +0200 Subject: [PATCH 083/385] Chaged prerelease to preview. --- .../Reflection/AssemblyDecoratorExtensionsTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs index 2adaa5507..672ebd186 100644 --- a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs @@ -69,7 +69,7 @@ public void GetProductVersion_ShouldReturnProductVersion() Assert.True(v.IsSemanticVersion()); Assert.True(v.HasAlphanumericVersion); Assert.Equal("6.0", v.ToVersion().ToString()); - Assert.Contains("-prerelease", v.ToString()); + Assert.Contains("-preview", v.ToString()); } [Fact] From 9bffc2e08f7726586ba473ee288cb9b68b6b4c7f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 31 Aug 2020 03:41:51 +0200 Subject: [PATCH 084/385] Enabled CI NuGet push --- azure-pipelines.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3e0f5c422..56144a85f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -156,8 +156,18 @@ jobs: nobuild: true - task: PublishBuildArtifacts@1 - displayName: 'Publish compiled DLLs' + condition: eq( variables['Agent.OS'], 'Windows_NT' ) + displayName: 'Store NuGet Packages' inputs: PathtoPublish: $(Build.ArtifactStagingDirectory) ArtifactName: Packages - publishLocation: Container \ No newline at end of file + publishLocation: Container + + - task: NuGetCommand@2 + condition: eq( variables['Agent.OS'], 'Windows_NT' ) + displayName: 'Publish NuGet Packages to https://nuget.cuemon.net/v3/index.json' + inputs: + command: 'push' + packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg' + nuGetFeedType: 'external' + publishFeedCredentials: 'Cuemon-Nuget' \ No newline at end of file From e0936dcc3db110151223a202980830b772598024 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 31 Aug 2020 04:53:53 +0200 Subject: [PATCH 085/385] Update README.md Added guide for early preview of Cuemon 6.0.0. --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 7d25a90ec..2fb2899af 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,23 @@ The release for now is planned to be 6.0.0. [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=Cuemon) +Want to try out the new and improved Cuemon? + +To consume a CI build, create a `NuGet.Config` in your root solution directory and add following content: + +```xml + + + + + + + + + + +``` + Stay tuned! Useful links for this project (will soon be changed for the forthcoming release): From dfe0a6179f9941bff5e0bd3cc8871455dcaa7056 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 31 Aug 2020 17:49:52 +0200 Subject: [PATCH 086/385] Extended wait. --- test/Cuemon.Core.Tests/DisposableTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Cuemon.Core.Tests/DisposableTest.cs b/test/Cuemon.Core.Tests/DisposableTest.cs index f2d0d8359..d94903113 100644 --- a/test/Cuemon.Core.Tests/DisposableTest.cs +++ b/test/Cuemon.Core.Tests/DisposableTest.cs @@ -169,7 +169,7 @@ public void UnmanagedDisposable_VerifyThatAssetIsBeingDisposedOnFinalize() GC.WaitForPendingFinalizers(); } - Thread.Sleep(500); + Thread.Sleep(1500); if (unmanaged.TryGetTarget(out var ud2)) { From 9106bcdc25d0eb324ce9e7b9b0cbbf48e9655fc3 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 31 Aug 2020 20:26:19 +0200 Subject: [PATCH 087/385] Minor pipeline adjustments. --- azure-pipelines.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 56144a85f..b6ccbd3bd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -24,6 +24,7 @@ jobs: steps: - task: UseDotNet@2 + condition: eq(variables['Agent.OS'], 'Linux') displayName: 'Use .Net Core SDK 2.2.207 (SonarCloud)' inputs: version: 2.2.207 @@ -148,7 +149,7 @@ jobs: pollingTimeoutSec: '300' - task: DotNetCoreCLI@2 - condition: eq( variables['Agent.OS'], 'Windows_NT' ) + condition: eq(variables['Agent.OS'], 'Windows_NT') displayName: dotnet pack inputs: command: pack @@ -156,7 +157,7 @@ jobs: nobuild: true - task: PublishBuildArtifacts@1 - condition: eq( variables['Agent.OS'], 'Windows_NT' ) + condition: eq(variables['Agent.OS'], 'Windows_NT') displayName: 'Store NuGet Packages' inputs: PathtoPublish: $(Build.ArtifactStagingDirectory) @@ -164,7 +165,7 @@ jobs: publishLocation: Container - task: NuGetCommand@2 - condition: eq( variables['Agent.OS'], 'Windows_NT' ) + condition: eq(variables['Agent.OS'], 'Windows_NT') displayName: 'Publish NuGet Packages to https://nuget.cuemon.net/v3/index.json' inputs: command: 'push' From 56fd8c0d05abec3ab9f4a1b0c8acbcc328df24e0 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 31 Aug 2020 20:26:51 +0200 Subject: [PATCH 088/385] Trying to get sourcelink to work. --- Directory.Build.props | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 169d9f1d1..504024a4a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -35,16 +35,13 @@ - - - all - runtime; build; native; contentfiles; analyzers - - + + - - + + + @@ -73,5 +70,4 @@ - \ No newline at end of file From d9c24726233ba204a525814efa735d04642dfc25 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 31 Aug 2020 20:38:30 +0200 Subject: [PATCH 089/385] Removed workaround for sourcelink. --- Directory.Build.targets | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 14fbb7992..45a61eebb 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,14 +1,4 @@  - - $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)')) - - - - - - - - + + \ No newline at end of file From 5ab62af2f25702cf7f01ce5d7b9d6021222599c6 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 31 Aug 2020 20:59:34 +0200 Subject: [PATCH 090/385] Enabled sourcelink with symbols. --- Directory.Build.props | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Directory.Build.props b/Directory.Build.props index 504024a4a..a0cba37e4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -26,6 +26,8 @@ en-US true true + true + snupkg true true $(MSBuildThisFileDirectory)cuemon.snk From 4de78a1fe85a0b5fac23a677d5c81fc8262d7bab Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 31 Aug 2020 23:27:31 +0200 Subject: [PATCH 091/385] Update azure-pipelines.yml for Azure Pipelines Updated to include Symbol packages. --- azure-pipelines.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b6ccbd3bd..6bbde76f2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -171,4 +171,13 @@ jobs: command: 'push' packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg' nuGetFeedType: 'external' + publishFeedCredentials: 'Cuemon-Nuget' + + - task: NuGetCommand@2 + condition: eq(variables['Agent.OS'], 'Windows_NT') + displayName: 'Publish NuGet Symbol Packages to https://nuget.cuemon.net/v3/index.json' + inputs: + command: 'push' + packagesToPush: '$(Build.ArtifactStagingDirectory)/*.snupkg' + nuGetFeedType: 'external' publishFeedCredentials: 'Cuemon-Nuget' \ No newline at end of file From 68c929fb0bfb730fd3811d9239a091d8f7705a48 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 1 Sep 2020 22:10:20 +0200 Subject: [PATCH 092/385] Removed redundant push of snupkg --- azure-pipelines.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6bbde76f2..b6ccbd3bd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -171,13 +171,4 @@ jobs: command: 'push' packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg' nuGetFeedType: 'external' - publishFeedCredentials: 'Cuemon-Nuget' - - - task: NuGetCommand@2 - condition: eq(variables['Agent.OS'], 'Windows_NT') - displayName: 'Publish NuGet Symbol Packages to https://nuget.cuemon.net/v3/index.json' - inputs: - command: 'push' - packagesToPush: '$(Build.ArtifactStagingDirectory)/*.snupkg' - nuGetFeedType: 'external' publishFeedCredentials: 'Cuemon-Nuget' \ No newline at end of file From 69e371b632b33e29254154529406404f15e68964 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 2 Sep 2020 19:05:29 +0200 Subject: [PATCH 093/385] Moved to be consistent with Cuemon.Core. --- src/Cuemon.Xml/{ => Data}/XmlDataReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/Cuemon.Xml/{ => Data}/XmlDataReader.cs (99%) diff --git a/src/Cuemon.Xml/XmlDataReader.cs b/src/Cuemon.Xml/Data/XmlDataReader.cs similarity index 99% rename from src/Cuemon.Xml/XmlDataReader.cs rename to src/Cuemon.Xml/Data/XmlDataReader.cs index ffaf5c632..576cf1910 100644 --- a/src/Cuemon.Xml/XmlDataReader.cs +++ b/src/Cuemon.Xml/Data/XmlDataReader.cs @@ -5,7 +5,7 @@ using Cuemon.Data; using Cuemon.Text; -namespace Cuemon.Xml +namespace Cuemon.Xml.Data { /// /// Provides a way of reading a forward-only stream of rows from an XML based data source. This class cannot be inherited. From 11982c34a805e0c344784644a3db2af74022c608 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 00:22:20 +0200 Subject: [PATCH 094/385] Code refactoring to Cuemon.IO (reduce dependency - keep Cuemon lightweight). --- .../IO/StreamDecoratorExtensions.cs | 218 +++------------- src/Cuemon.Core/Properties/AssemblyInfo.cs | 4 +- src/Cuemon.Core/Security/Cryptography/Hash.cs | 2 +- src/Cuemon.Core/Text/ByteOrderMark.cs | 2 +- src/Cuemon.Extensions.IO/StreamExtensions.cs | 32 +-- .../Cuemon.Extensions.Newtonsoft.Json.csproj | 1 + .../Cuemon.Extensions.Xml.csproj | 1 + .../AsyncStreamCompressionOptions.cs | 38 +++ src/Cuemon.IO/AsyncStreamCopyOptions.cs | 51 ++++ src/Cuemon.IO/AsyncStreamEncodingOptions.cs | 50 ++++ src/Cuemon.IO/AsyncStreamOptions.cs | 18 ++ src/Cuemon.IO/AsyncStreamReaderOptions.cs | 46 ++++ .../Extensions/StreamDecoratorExtensions.cs | 236 ++++++++++++++++-- .../IO => Cuemon.IO}/InternalStreamWriter.cs | 0 .../StreamCompressionOptions.cs | 0 .../IO => Cuemon.IO}/StreamCopyOptions.cs | 0 .../IO => Cuemon.IO}/StreamEncodingOptions.cs | 0 .../IO => Cuemon.IO}/StreamFactory.cs | 0 .../IO => Cuemon.IO}/StreamOptions.cs | 0 .../IO => Cuemon.IO}/StreamReaderOptions.cs | 0 .../IO => Cuemon.IO}/StreamWriterOptions.cs | 0 src/Cuemon.Net/Cuemon.Net.csproj | 1 + .../Cuemon.Core.Tests.csproj | 1 + test/Cuemon.Xml.Tests/Cuemon.Xml.Tests.csproj | 1 + 24 files changed, 482 insertions(+), 220 deletions(-) create mode 100644 src/Cuemon.IO/AsyncStreamCompressionOptions.cs create mode 100644 src/Cuemon.IO/AsyncStreamCopyOptions.cs create mode 100644 src/Cuemon.IO/AsyncStreamEncodingOptions.cs create mode 100644 src/Cuemon.IO/AsyncStreamOptions.cs create mode 100644 src/Cuemon.IO/AsyncStreamReaderOptions.cs rename src/{Cuemon.Core/IO => Cuemon.IO}/InternalStreamWriter.cs (100%) rename src/{Cuemon.Core/IO => Cuemon.IO}/StreamCompressionOptions.cs (100%) rename src/{Cuemon.Core/IO => Cuemon.IO}/StreamCopyOptions.cs (100%) rename src/{Cuemon.Core/IO => Cuemon.IO}/StreamEncodingOptions.cs (100%) rename src/{Cuemon.Core/IO => Cuemon.IO}/StreamFactory.cs (100%) rename src/{Cuemon.Core/IO => Cuemon.IO}/StreamOptions.cs (100%) rename src/{Cuemon.Core/IO => Cuemon.IO}/StreamReaderOptions.cs (100%) rename src/{Cuemon.Core/IO => Cuemon.IO}/StreamWriterOptions.cs (100%) diff --git a/src/Cuemon.Core/Extensions/IO/StreamDecoratorExtensions.cs b/src/Cuemon.Core/Extensions/IO/StreamDecoratorExtensions.cs index 5586a15c4..64324a327 100644 --- a/src/Cuemon.Core/Extensions/IO/StreamDecoratorExtensions.cs +++ b/src/Cuemon.Core/Extensions/IO/StreamDecoratorExtensions.cs @@ -1,217 +1,61 @@ -using System; -using System.ComponentModel; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.Text; +using System.IO; namespace Cuemon.IO { - /// - /// Extension methods for the class tailored to adhere the decorator pattern. - /// - /// - /// - public static class StreamDecoratorExtensions + internal static class StreamDecoratorExtensions { - /// - /// Converts the enclosed of the specified to a . - /// - /// The to extend. - /// The which may be configured. - /// A containing the result of the enclosed of the specified . - /// will be initialized with and . - /// - /// cannot be null. - /// - /// - /// was initialized with an invalid . - /// - public static string ToEncodedString(this IDecorator decorator, Action setup = null) + internal static void CopyStreamCore(this IDecorator decorator, Stream destination, int bufferSize = 81920, bool changePosition = true) { - Validator.ThrowIfNull(decorator, nameof(decorator)); - var options = Patterns.Configure(setup); - if (options.Encoding.Equals(EncodingOptions.DefaultEncoding)) { options.Encoding = ByteOrderMark.DetectEncodingOrDefault(decorator.Inner, options.Encoding); } - if (options.Preamble < PreambleSequence.Keep || options.Preamble > PreambleSequence.Remove) { throw new InvalidEnumArgumentException(nameof(setup), (int)options.Preamble, typeof(PreambleSequence)); } - - var bytes = Decorator.Enclose(decorator.Inner).ToByteArray(o => - { - o.BufferSize = options.BufferSize; - o.LeaveOpen = options.LeaveOpen; - }); - return Convertible.ToString(bytes, o => + var source = decorator.Inner; + long lastPosition = 0; + if (changePosition && source.CanSeek) { - o.Encoding = options.Encoding; - o.Preamble = options.Preamble; - }); - } + lastPosition = source.Position; + if (source.CanSeek) { source.Position = 0; } + } - /// - /// Converts the enclosed of the specified to a . - /// - /// The to extend. - /// The which may be configured. - /// A task that represents the asynchronous operation. The task result contains a containing the result of the enclosed of the specified . - /// will be initialized with and . - /// - /// cannot be null. - /// - /// - /// was initialized with an invalid . - /// - public static Task ToEncodedStringAsync(this IDecorator decorator, Action setup = null) - { - Validator.ThrowIfNull(decorator, nameof(decorator)); - var options = Patterns.Configure(setup); - if (options.Encoding.Equals(EncodingOptions.DefaultEncoding)) { options.Encoding = ByteOrderMark.DetectEncodingOrDefault(decorator.Inner, options.Encoding); } - if (options.Preamble < PreambleSequence.Keep || options.Preamble > PreambleSequence.Remove) { throw new InvalidEnumArgumentException(nameof(setup), (int)options.Preamble, typeof(PreambleSequence)); } - return ToEncodedStringAsyncCore(decorator, options); - } + source.CopyTo(destination, bufferSize); + destination.Flush(); - private static async Task ToEncodedStringAsyncCore(this IDecorator decorator, StreamReaderOptions options) - { - var bytes = await Decorator.Enclose(decorator.Inner).ToByteArrayAsync(o => - { - o.BufferSize = options.BufferSize; - o.LeaveOpen = options.LeaveOpen; - o.CancellationToken = options.CancellationToken; - }).ConfigureAwait(false); - return Convertible.ToString(bytes, o => - { - o.Encoding = options.Encoding; - o.Preamble = options.Preamble; - }); + if (changePosition && source.CanSeek) { source.Position = lastPosition; } + if (changePosition && destination.CanSeek) { destination.Position = 0; } } - /// - /// Converts the enclosed of the specified to its equivalent representation. - /// - /// The to extend. - /// The which may be configured. - /// A that is equivalent to the enclosed of the specified . - /// - /// cannot be null. - /// - /// - /// The enclosed of cannot be read from. - /// - public static byte[] ToByteArray(this IDecorator decorator, Action setup = null) + internal static byte[] ToByteArrayCore(this IDecorator decorator, int bufferSize = 81920, bool leaveOpen = false) { Validator.ThrowIfNull(decorator, nameof(decorator)); Validator.ThrowIfFalse(decorator.Inner.CanRead, nameof(decorator.Inner), "Stream cannot be read from."); - var options = Patterns.Configure(setup); try { - if (decorator.Inner is MemoryStream s) { return s.ToArray(); } - using (var memoryStream = new MemoryStream(new byte[decorator.Inner.Length])) + if (decorator.Inner is MemoryStream s) { - var oldPosition = decorator.Inner.Position; - if (decorator.Inner.CanSeek) { decorator.Inner.Position = 0; } - decorator.Inner.CopyTo(memoryStream, options.BufferSize); - if (decorator.Inner.CanSeek) { decorator.Inner.Position = oldPosition; } - return memoryStream.ToArray(); + return s.ToArray(); } - } - finally - { - if (!options.LeaveOpen) { decorator.Inner.Dispose(); } - } - } - /// - /// Converts the enclosed of the specified to its equivalent representation. - /// - /// The to extend. - /// The which may be configured. - /// A task that represents the asynchronous operation. The task result contains a that is equivalent to the enclosed of the specified . - /// - /// cannot be null. - /// - /// - /// The enclosed of cannot be read from. - /// - public static Task ToByteArrayAsync(this IDecorator decorator, Action setup = null) - { - Validator.ThrowIfNull(decorator, nameof(decorator)); - Validator.ThrowIfFalse(decorator.Inner.CanRead, nameof(decorator.Inner), "Stream cannot be read from."); - return ToByteArrayAsyncCore(decorator, Patterns.Configure(setup)); - } - - private static async Task ToByteArrayAsyncCore(this IDecorator decorator, StreamCopyOptions options) - { - try - { - if (decorator.Inner is MemoryStream s) { return s.ToArray(); } using (var memoryStream = new MemoryStream(new byte[decorator.Inner.Length])) { var oldPosition = decorator.Inner.Position; - if (decorator.Inner.CanSeek) { decorator.Inner.Position = 0; } - await decorator.Inner.CopyToAsync(memoryStream, options.BufferSize, options.CancellationToken).ConfigureAwait(false); - if (decorator.Inner.CanSeek) { decorator.Inner.Position = oldPosition; } + if (decorator.Inner.CanSeek) + { + decorator.Inner.Position = 0; + } + + decorator.Inner.CopyTo(memoryStream, bufferSize); + if (decorator.Inner.CanSeek) + { + decorator.Inner.Position = oldPosition; + } + return memoryStream.ToArray(); } } finally { - if (!options.LeaveOpen) { decorator.Inner.Dispose(); } - } - } - - /// - /// Reads the bytes from the enclosed of the specified and writes them to the . - /// - /// The to extend. - /// The to which the contents of the current stream will be copied. - /// The size of the buffer. This value must be greater than zero. The default size is 81920. - /// if true, the enclosed of the specified will temporarily have its position changed to 0; otherwise the position is left untouched. - /// - /// cannot be null. - /// - public static void CopyStream(this IDecorator decorator, Stream destination, int bufferSize = 81920, bool changePosition = true) - { - Validator.ThrowIfNull(decorator, nameof(decorator)); - var source = decorator.Inner; - long lastPosition = 0; - if (changePosition && source.CanSeek) - { - lastPosition = source.Position; - if (source.CanSeek) { source.Position = 0; } - } - - source.CopyTo(destination, bufferSize); - destination.Flush(); - - if (changePosition && source.CanSeek) { source.Position = lastPosition; } - if (changePosition && destination.CanSeek) { destination.Position = 0; } - } - - /// - /// Asynchronously reads the bytes from the enclosed of the specified and writes them to the . - /// - /// The to extend. - /// The to which the contents of the current stream will be copied. - /// The size of the buffer. This value must be greater than zero. The default size is 81920. - /// The token to monitor for cancellation requests. The default value is . - /// if true, the enclosed of the specified will temporarily have its position changed to 0; otherwise the position is left untouched. - /// - /// cannot be null. - /// - public static async Task CopyStreamAsync(this IDecorator decorator, Stream destination, int bufferSize = 81920, CancellationToken ct = default, bool changePosition = true) - { - Validator.ThrowIfNull(decorator, nameof(decorator)); - var source = decorator.Inner; - long lastPosition = 0; - if (changePosition && source.CanSeek) - { - lastPosition = source.Position; - if (source.CanSeek) { source.Position = 0; } + if (!leaveOpen) + { + decorator.Inner.Dispose(); + } } - - await source.CopyToAsync(destination, bufferSize, ct).ConfigureAwait(false); - await destination.FlushAsync(ct).ConfigureAwait(false); - - if (changePosition && source.CanSeek) { source.Position = lastPosition; } - if (changePosition && destination.CanSeek) { destination.Position = 0; } } } } \ No newline at end of file diff --git a/src/Cuemon.Core/Properties/AssemblyInfo.cs b/src/Cuemon.Core/Properties/AssemblyInfo.cs index a905199ee..ddd438e05 100644 --- a/src/Cuemon.Core/Properties/AssemblyInfo.cs +++ b/src/Cuemon.Core/Properties/AssemblyInfo.cs @@ -1,4 +1,6 @@ -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; [assembly: ComVisible(false)] +[assembly: InternalsVisibleTo("Cuemon.IO, PublicKey=00240000048000009400000006020000002400005253413100040000010001002F66D8473F676F4E7B47400527D33951A774422DFFC3DF6D7F87C82E5694E9F3AA626D36BEBEA428AD5B800EFCF6CE87B73268F5A0125A7D38739D344703A1C48785AC1A45B1C27EDFDF2EB30BA2B3E3CEA92E5981C30F3A95685A680B7EBEE66F422D176CD1623019D5A05770B9BA498144B1134593BEA6F674F334CF2B90B0")] [assembly: Guid("989939cf-cef2-4e23-8bce-725255e35ce6")] \ No newline at end of file diff --git a/src/Cuemon.Core/Security/Cryptography/Hash.cs b/src/Cuemon.Core/Security/Cryptography/Hash.cs index 9f7e511f3..4cabf26e5 100644 --- a/src/Cuemon.Core/Security/Cryptography/Hash.cs +++ b/src/Cuemon.Core/Security/Cryptography/Hash.cs @@ -266,7 +266,7 @@ public virtual HashResult ComputeHash(Stream input) { return ComputeHash(Disposable.SafeInvoke(() => new MemoryStream(), destination => { - Decorator.Enclose(input).CopyStream(destination); + Decorator.Enclose(input).CopyStreamCore(destination); return destination; }).ToArray()); } diff --git a/src/Cuemon.Core/Text/ByteOrderMark.cs b/src/Cuemon.Core/Text/ByteOrderMark.cs index b8e07f406..67735fade 100644 --- a/src/Cuemon.Core/Text/ByteOrderMark.cs +++ b/src/Cuemon.Core/Text/ByteOrderMark.cs @@ -156,7 +156,7 @@ public static Stream Remove(Stream value, Encoding encoding, Action o.LeaveOpen = option.LeaveOpen); + var bytes = Decorator.Enclose(value).ToByteArrayCore(leaveOpen: option.LeaveOpen); bytes = Remove(bytes, encoding); return Disposable.SafeInvoke(() => new MemoryStream(bytes.Length), ms => { diff --git a/src/Cuemon.Extensions.IO/StreamExtensions.cs b/src/Cuemon.Extensions.IO/StreamExtensions.cs index b8d2452eb..17a017158 100644 --- a/src/Cuemon.Extensions.IO/StreamExtensions.cs +++ b/src/Cuemon.Extensions.IO/StreamExtensions.cs @@ -106,7 +106,7 @@ public static byte[] ToByteArray(this Stream input, Action se /// Converts the specified to its equivalent representation. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A task that represents the asynchronous operation. The task result contains a that is equivalent to . /// /// cannot be null. @@ -114,7 +114,7 @@ public static byte[] ToByteArray(this Stream input, Action se /// /// cannot be read from. /// - public static Task ToByteArrayAsync(this Stream input, Action setup = null) + public static Task ToByteArrayAsync(this Stream input, Action setup = null) { Validator.ThrowIfNull(input, nameof(input)); Validator.ThrowIfFalse(input.CanRead, nameof(input), "Stream cannot be read from."); @@ -182,7 +182,7 @@ public static string ToEncodedString(this Stream value, Action to a . /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A task that represents the asynchronous operation. The task result contains a containing the result of the specified . /// will be initialized with and . /// @@ -191,7 +191,7 @@ public static string ToEncodedString(this Stream value, Action /// was initialized with an invalid . /// - public static Task ToEncodedStringAsync(this Stream value, Action setup = null) + public static Task ToEncodedStringAsync(this Stream value, Action setup = null) { Validator.ThrowIfNull(value, nameof(value)); return Decorator.Enclose(value).ToEncodedStringAsync(setup); @@ -220,7 +220,7 @@ public static Stream CompressBrotli(this Stream value, Action using the BROTLI algorithm. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A DEFLATE compressed of the . /// A task that represents the asynchronous operation. The task result contains a BROTLI compressed of the specified . /// @@ -229,7 +229,7 @@ public static Stream CompressBrotli(this Stream value, Action /// does not support write operations such as compression. /// - public static Task CompressBrotliAsync(this Stream value, Action setup = null) + public static Task CompressBrotliAsync(this Stream value, Action setup = null) { Validator.ThrowIfNull(value, nameof(value)); return Decorator.Enclose(value).CompressBrotliAsync(setup); @@ -258,7 +258,7 @@ public static Stream CompressDeflate(this Stream value, Action using the DEFLATE algorithm. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A DEFLATE compressed of the . /// A task that represents the asynchronous operation. The task result contains a DEFLATE compressed of the specified . /// @@ -267,7 +267,7 @@ public static Stream CompressDeflate(this Stream value, Action /// does not support write operations such as compression. /// - public static Task CompressDeflateAsync(this Stream value, Action setup = null) + public static Task CompressDeflateAsync(this Stream value, Action setup = null) { Validator.ThrowIfNull(value, nameof(value)); return Decorator.Enclose(value).CompressDeflateAsync(setup); @@ -295,7 +295,7 @@ public static Stream CompressGZip(this Stream value, Action using the GZIP algorithm. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A DEFLATE compressed of the . /// A task that represents the asynchronous operation. The task result contains a GZIP compressed of the specified . /// @@ -304,7 +304,7 @@ public static Stream CompressGZip(this Stream value, Action /// does not support write operations such as compression. /// - public static Task CompressGZipAsync(this Stream value, Action setup = null) + public static Task CompressGZipAsync(this Stream value, Action setup = null) { Validator.ThrowIfNull(value, nameof(value)); return Decorator.Enclose(value).CompressGZipAsync(setup); @@ -336,7 +336,7 @@ public static Stream DecompressBrotli(this Stream value, Action using the BROTLI data format specification. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A decompressed of the . /// A task that represents the asynchronous operation. The task result contains a decompressed of the specified . /// @@ -348,7 +348,7 @@ public static Stream DecompressBrotli(this Stream value, Action /// was compressed using an unsupported compression method. /// - public static Task DecompressBrotliAsync(this Stream value, Action setup = null) + public static Task DecompressBrotliAsync(this Stream value, Action setup = null) { Validator.ThrowIfNull(value, nameof(value)); return Decorator.Enclose(value).DecompressBrotliAsync(setup); @@ -380,7 +380,7 @@ public static Stream DecompressDeflate(this Stream value, Action using the DEFLATE data format specification. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A decompressed of the . /// A task that represents the asynchronous operation. The task result contains a decompressed of the specified . /// @@ -392,7 +392,7 @@ public static Stream DecompressDeflate(this Stream value, Action /// was compressed using an unsupported compression method. /// - public static Task DecompressDeflateAsync(this Stream value, Action setup = null) + public static Task DecompressDeflateAsync(this Stream value, Action setup = null) { Validator.ThrowIfNull(value, nameof(value)); return Decorator.Enclose(value).DecompressDeflateAsync(setup); @@ -423,7 +423,7 @@ public static Stream DecompressGZip(this Stream value, Action /// Decompresses the using the GZIP data format specification. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A decompressed of the . /// A task that represents the asynchronous operation. The task result contains a decompressed of the specified . /// @@ -435,7 +435,7 @@ public static Stream DecompressGZip(this Stream value, Action /// /// was compressed using an unsupported compression method. /// - public static Task DecompressGZipAsync(this Stream value, Action setup = null) + public static Task DecompressGZipAsync(this Stream value, Action setup = null) { Validator.ThrowIfNull(value, nameof(value)); return Decorator.Enclose(value).DecompressGZipAsync(setup); diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj index 51739f243..e9710f861 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj index 92ec641c1..e0335c8ed 100644 --- a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj +++ b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Cuemon.IO/AsyncStreamCompressionOptions.cs b/src/Cuemon.IO/AsyncStreamCompressionOptions.cs new file mode 100644 index 000000000..753d4c383 --- /dev/null +++ b/src/Cuemon.IO/AsyncStreamCompressionOptions.cs @@ -0,0 +1,38 @@ +using System.IO; +using System.IO.Compression; + +namespace Cuemon.IO +{ + /// + /// Configuration options for compressed . + /// + public class AsyncStreamCompressionOptions : AsyncStreamCopyOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// + /// + /// + /// + public AsyncStreamCompressionOptions() + { + Level = CompressionLevel.Optimal; + } + + /// + /// Gets or sets the enumeration values that indicates whether to emphasize speed or compression efficiency when compressing the stream. + /// + /// The level of the compression. + public CompressionLevel Level { get; set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.IO/AsyncStreamCopyOptions.cs b/src/Cuemon.IO/AsyncStreamCopyOptions.cs new file mode 100644 index 000000000..39bd1dabd --- /dev/null +++ b/src/Cuemon.IO/AsyncStreamCopyOptions.cs @@ -0,0 +1,51 @@ +using System; +using System.IO; + +namespace Cuemon.IO +{ + /// + /// Configuration options that is related to copy operations. + /// + public class AsyncStreamCopyOptions : AsyncDisposableOptions + { + private int _bufferSize; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// 81920 + /// + /// + /// + public AsyncStreamCopyOptions() + { + BufferSize = 81920; + } + + /// + /// Gets or sets the size of the buffer. + /// + /// The size of the buffer. + /// + /// is lower than or equal to 0. + /// + public int BufferSize + { + get => _bufferSize; + set + { + Validator.ThrowIfLowerThanOrEqual(value, 0, nameof(value)); + _bufferSize = value; + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.IO/AsyncStreamEncodingOptions.cs b/src/Cuemon.IO/AsyncStreamEncodingOptions.cs new file mode 100644 index 000000000..5c99b5a7f --- /dev/null +++ b/src/Cuemon.IO/AsyncStreamEncodingOptions.cs @@ -0,0 +1,50 @@ +using System.IO; +using System.Text; +using Cuemon.Text; + +namespace Cuemon.IO +{ + /// + /// Configuration options for . + /// + public class AsyncStreamEncodingOptions : AsyncStreamOptions, IEncodingOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public AsyncStreamEncodingOptions() + { + Encoding = EncodingOptions.DefaultEncoding; + Preamble = EncodingOptions.DefaultPreambleSequence; + } + + /// + /// Gets or sets the action to take in regards to encoding related preamble sequences. + /// + /// A value that indicates whether to preserve or remove preamble sequences. + public PreambleSequence Preamble { get; set; } + + /// + /// Gets or sets the character encoding to use for the operation. + /// + /// The character encoding to use for the operation. + public Encoding Encoding { get; set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.IO/AsyncStreamOptions.cs b/src/Cuemon.IO/AsyncStreamOptions.cs new file mode 100644 index 000000000..cc44c2f7f --- /dev/null +++ b/src/Cuemon.IO/AsyncStreamOptions.cs @@ -0,0 +1,18 @@ +using System.IO; + +namespace Cuemon.IO +{ + /// + /// Configuration options for . + /// + public class AsyncStreamOptions : AsyncDisposableOptions + { + + /// + /// Initializes a new instance of the class. + /// + public AsyncStreamOptions() + { + } + } +} \ No newline at end of file diff --git a/src/Cuemon.IO/AsyncStreamReaderOptions.cs b/src/Cuemon.IO/AsyncStreamReaderOptions.cs new file mode 100644 index 000000000..01e9260b8 --- /dev/null +++ b/src/Cuemon.IO/AsyncStreamReaderOptions.cs @@ -0,0 +1,46 @@ +using System.IO; +using Cuemon.Text; + +namespace Cuemon.IO +{ + /// + /// Configuration options for . + /// + public class AsyncStreamReaderOptions : AsyncStreamEncodingOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// 81920 + /// + /// + /// + public AsyncStreamReaderOptions() + { + BufferSize = 81920; + } + + /// + /// Gets or sets the minimum size of the buffer. + /// + /// The minimum size of the buffer. + public int BufferSize { get; set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs b/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs index 0035a4720..f795e4ae5 100644 --- a/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs +++ b/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs @@ -1,7 +1,10 @@ using System; +using System.ComponentModel; using System.IO; using System.IO.Compression; +using System.Threading; using System.Threading.Tasks; +using Cuemon.Text; namespace Cuemon.IO { @@ -12,6 +15,209 @@ namespace Cuemon.IO /// public static class StreamDecoratorExtensions { + /// + /// Reads the bytes from the enclosed of the specified and writes them to the . + /// + /// The to extend. + /// The to which the contents of the current stream will be copied. + /// The size of the buffer. This value must be greater than zero. The default size is 81920. + /// if true, the enclosed of the specified will temporarily have its position changed to 0; otherwise the position is left untouched. + /// + /// cannot be null. + /// + public static void CopyStream(this IDecorator decorator, Stream destination, int bufferSize = 81920, bool changePosition = true) + { + Validator.ThrowIfNull(decorator, nameof(decorator)); + decorator.CopyStreamCore(destination, bufferSize, changePosition); + } + + /// + /// Asynchronously reads the bytes from the enclosed of the specified and writes them to the . + /// + /// The to extend. + /// The to which the contents of the current stream will be copied. + /// The size of the buffer. This value must be greater than zero. The default size is 81920. + /// The token to monitor for cancellation requests. The default value is . + /// if true, the enclosed of the specified will temporarily have its position changed to 0; otherwise the position is left untouched. + /// + /// cannot be null. + /// + public static async Task CopyStreamAsync(this IDecorator decorator, Stream destination, int bufferSize = 81920, CancellationToken ct = default, bool changePosition = true) + { + Validator.ThrowIfNull(decorator, nameof(decorator)); + var source = decorator.Inner; + long lastPosition = 0; + if (changePosition && source.CanSeek) + { + lastPosition = source.Position; + if (source.CanSeek) { source.Position = 0; } + } + + await source.CopyToAsync(destination, bufferSize, ct).ConfigureAwait(false); + await destination.FlushAsync(ct).ConfigureAwait(false); + + if (changePosition && source.CanSeek) { source.Position = lastPosition; } + if (changePosition && destination.CanSeek) { destination.Position = 0; } + } + + /// + /// Converts the enclosed of the specified to its equivalent representation. + /// + /// The to extend. + /// The which may be configured. + /// A that is equivalent to the enclosed of the specified . + /// + /// cannot be null. + /// + /// + /// The enclosed of cannot be read from. + /// + public static byte[] ToByteArray(this IDecorator decorator, Action setup = null) + { + Validator.ThrowIfNull(decorator, nameof(decorator)); + Validator.ThrowIfFalse(decorator.Inner.CanRead, nameof(decorator.Inner), "Stream cannot be read from."); + var options = Patterns.Configure(setup); + return decorator.ToByteArrayCore(options.BufferSize, options.LeaveOpen); + } + + /// + /// Converts the enclosed of the specified to its equivalent representation. + /// + /// The to extend. + /// The which may be configured. + /// A task that represents the asynchronous operation. The task result contains a that is equivalent to the enclosed of the specified . + /// + /// cannot be null. + /// + /// + /// The enclosed of cannot be read from. + /// + public static Task ToByteArrayAsync(this IDecorator decorator, Action setup = null) + { + Validator.ThrowIfNull(decorator, nameof(decorator)); + Validator.ThrowIfFalse(decorator.Inner.CanRead, nameof(decorator.Inner), "Stream cannot be read from."); + return ToByteArrayAsyncCore(decorator, Patterns.Configure(setup)); + } + + private static async Task ToByteArrayAsyncCore(this IDecorator decorator, AsyncStreamCopyOptions options) + { + try + { + if (decorator.Inner is MemoryStream s) + { + return s.ToArray(); + } + + using (var memoryStream = new MemoryStream(new byte[decorator.Inner.Length])) + { + var oldPosition = decorator.Inner.Position; + if (decorator.Inner.CanSeek) + { + decorator.Inner.Position = 0; + } + + await decorator.Inner.CopyToAsync(memoryStream, options.BufferSize, options.CancellationToken).ConfigureAwait(false); + if (decorator.Inner.CanSeek) + { + decorator.Inner.Position = oldPosition; + } + + return memoryStream.ToArray(); + } + } + finally + { + if (!options.LeaveOpen) + { + decorator.Inner.Dispose(); + } + } + } + + /// + /// Converts the enclosed of the specified to a . + /// + /// The to extend. + /// The which may be configured. + /// A containing the result of the enclosed of the specified . + /// will be initialized with and . + /// + /// cannot be null. + /// + /// + /// was initialized with an invalid . + /// + public static string ToEncodedString(this IDecorator decorator, Action setup = null) + { + Validator.ThrowIfNull(decorator, nameof(decorator)); + var options = Patterns.Configure(setup); + if (options.Encoding.Equals(EncodingOptions.DefaultEncoding)) + { + options.Encoding = ByteOrderMark.DetectEncodingOrDefault(decorator.Inner, options.Encoding); + } + + if (options.Preamble < PreambleSequence.Keep || options.Preamble > PreambleSequence.Remove) + { + throw new InvalidEnumArgumentException(nameof(setup), (int) options.Preamble, typeof(PreambleSequence)); + } + + var bytes = Decorator.Enclose(decorator.Inner).ToByteArray(o => + { + o.BufferSize = options.BufferSize; + o.LeaveOpen = options.LeaveOpen; + }); + return Convertible.ToString(bytes, o => + { + o.Encoding = options.Encoding; + o.Preamble = options.Preamble; + }); + } + + /// + /// Converts the enclosed of the specified to a . + /// + /// The to extend. + /// The which may be configured. + /// A task that represents the asynchronous operation. The task result contains a containing the result of the enclosed of the specified . + /// will be initialized with and . + /// + /// cannot be null. + /// + /// + /// was initialized with an invalid . + /// + public static Task ToEncodedStringAsync(this IDecorator decorator, Action setup = null) + { + Validator.ThrowIfNull(decorator, nameof(decorator)); + var options = Patterns.Configure(setup); + if (options.Encoding.Equals(EncodingOptions.DefaultEncoding)) + { + options.Encoding = ByteOrderMark.DetectEncodingOrDefault(decorator.Inner, options.Encoding); + } + + if (options.Preamble < PreambleSequence.Keep || options.Preamble > PreambleSequence.Remove) + { + throw new InvalidEnumArgumentException(nameof(setup), (int) options.Preamble, typeof(PreambleSequence)); + } + + return ToEncodedStringAsyncCore(decorator, options); + } + + private static async Task ToEncodedStringAsyncCore(this IDecorator decorator, AsyncStreamReaderOptions options) + { + var bytes = await Decorator.Enclose(decorator.Inner).ToByteArrayAsync(o => + { + o.BufferSize = options.BufferSize; + o.LeaveOpen = options.LeaveOpen; + o.CancellationToken = options.CancellationToken; + }).ConfigureAwait(false); + return Convertible.ToString(bytes, o => + { + o.Encoding = options.Encoding; + o.Preamble = options.Preamble; + }); + } + #if NETSTANDARD2_1 /// /// Compress the enclosed of the specified using the Brotli algorithm. @@ -35,7 +241,7 @@ public static Stream CompressBrotli(this IDecorator decorator, Action of the specified using the Brotli algorithm. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A task that represents the asynchronous operation. The task result contains a compressed version of the enclosed of the specified . /// /// cannot be null. @@ -43,7 +249,7 @@ public static Stream CompressBrotli(this IDecorator decorator, Action /// The enclosed of does not support write operations such as compression. /// - public static Task CompressBrotliAsync(this IDecorator decorator, Action setup = null) + public static Task CompressBrotliAsync(this IDecorator decorator, Action setup = null) { Validator.ThrowIfNull(decorator, nameof(decorator)); return CompressAsync(decorator, Patterns.Configure(setup), (stream, level, leaveOpen) => new BrotliStream(stream, level, leaveOpen)); @@ -74,7 +280,7 @@ public static Stream DecompressBrotli(this IDecorator decorator, Action< /// Decompress the enclosed of the specified using Brotli data format specification. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A task that represents the asynchronous operation. The task result contains a decompressed version of the enclosed of the specified . /// /// cannot be null. @@ -85,7 +291,7 @@ public static Stream DecompressBrotli(this IDecorator decorator, Action< /// /// The enclosed of was compressed using an unsupported compression method. /// - public static Task DecompressBrotliAsync(this IDecorator decorator, Action setup = null) + public static Task DecompressBrotliAsync(this IDecorator decorator, Action setup = null) { Validator.ThrowIfNull(decorator, nameof(decorator)); return DecompressAsync(decorator, Patterns.Configure(setup), (stream, mode, leaveOpen) => new BrotliStream(stream, mode, leaveOpen)); @@ -114,7 +320,7 @@ public static Stream CompressGZip(this IDecorator decorator, Action of the specified using the GZip algorithm. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A task that represents the asynchronous operation. The task result contains a compressed version of the enclosed of the specified . /// /// cannot be null. @@ -122,7 +328,7 @@ public static Stream CompressGZip(this IDecorator decorator, Action /// The enclosed of does not support write operations such as compression. /// - public static Task CompressGZipAsync(this IDecorator decorator, Action setup = null) + public static Task CompressGZipAsync(this IDecorator decorator, Action setup = null) { Validator.ThrowIfNull(decorator, nameof(decorator)); return CompressAsync(decorator, Patterns.Configure(setup), (stream, level, leaveOpen) => new GZipStream(stream, level, leaveOpen)); @@ -153,7 +359,7 @@ public static Stream DecompressGZip(this IDecorator decorator, Action of the specified using GZip data format specification. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A task that represents the asynchronous operation. The task result contains a decompressed version of the enclosed of the specified . /// /// cannot be null. @@ -164,7 +370,7 @@ public static Stream DecompressGZip(this IDecorator decorator, Action /// The enclosed of was compressed using an unsupported compression method. /// - public static Task DecompressGZipAsync(this IDecorator decorator, Action setup = null) + public static Task DecompressGZipAsync(this IDecorator decorator, Action setup = null) { Validator.ThrowIfNull(decorator, nameof(decorator)); return DecompressAsync(decorator, Patterns.Configure(setup), (stream, mode, leaveOpen) => new GZipStream(stream, mode, leaveOpen)); @@ -192,7 +398,7 @@ public static Stream CompressDeflate(this IDecorator decorator, Action of the specified using the Deflate algorithm. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A task that represents the asynchronous operation. The task result contains a compressed version of the enclosed of the specified . /// /// cannot be null. @@ -200,7 +406,7 @@ public static Stream CompressDeflate(this IDecorator decorator, Action /// The enclosed of does not support write operations such as compression. /// - public static Task CompressDeflateAsync(this IDecorator decorator, Action setup = null) + public static Task CompressDeflateAsync(this IDecorator decorator, Action setup = null) { Validator.ThrowIfNull(decorator, nameof(decorator)); return CompressAsync(decorator, Patterns.Configure(setup), (stream, level, leaveOpen) => new DeflateStream(stream, level, leaveOpen)); @@ -231,7 +437,7 @@ public static Stream DecompressDeflate(this IDecorator decorator, Action /// Decompress the enclosed of the specified using Deflate data format specification. /// /// The to extend. - /// The which may be configured. + /// The which may be configured. /// A task that represents the asynchronous operation. The task result contains a decompressed version of the enclosed of the specified . /// /// cannot be null. @@ -242,7 +448,7 @@ public static Stream DecompressDeflate(this IDecorator decorator, Action /// /// The enclosed of was compressed using an unsupported compression method. /// - public static Task DecompressDeflateAsync(this IDecorator decorator, Action setup = null) + public static Task DecompressDeflateAsync(this IDecorator decorator, Action setup = null) { Validator.ThrowIfNull(decorator, nameof(decorator)); return DecompressAsync(decorator, Patterns.Configure(setup), (stream, mode, leaveOpen) => new DeflateStream(stream, mode, leaveOpen)); @@ -256,13 +462,14 @@ private static Stream Compress(IDecorator decorator, StreamCompressio { Decorator.Enclose(decorator.Inner).CopyStream(compressed, options.BufferSize); } + target.Flush(); target.Position = 0; return target; }); } - private static Task CompressAsync(IDecorator decorator, StreamCompressionOptions options, Func decompressor) where T : Stream + private static Task CompressAsync(IDecorator decorator, AsyncStreamCompressionOptions options, Func decompressor) where T : Stream { return Disposable.SafeInvokeAsync(() => new MemoryStream(), async (target, ct) => { @@ -291,13 +498,14 @@ private static Stream Decompress(IDecorator decorator, StreamCopyOpti { Decorator.Enclose(uncompressed).CopyStream(target, options.BufferSize); } + target.Flush(); target.Position = 0; return target; }); } - private static Task DecompressAsync(IDecorator decorator, StreamCopyOptions options, Func compressor) where T : Stream + private static Task DecompressAsync(IDecorator decorator, AsyncStreamCopyOptions options, Func compressor) where T : Stream { return Disposable.SafeInvokeAsync(() => new MemoryStream(), async (target, ct) => { diff --git a/src/Cuemon.Core/IO/InternalStreamWriter.cs b/src/Cuemon.IO/InternalStreamWriter.cs similarity index 100% rename from src/Cuemon.Core/IO/InternalStreamWriter.cs rename to src/Cuemon.IO/InternalStreamWriter.cs diff --git a/src/Cuemon.Core/IO/StreamCompressionOptions.cs b/src/Cuemon.IO/StreamCompressionOptions.cs similarity index 100% rename from src/Cuemon.Core/IO/StreamCompressionOptions.cs rename to src/Cuemon.IO/StreamCompressionOptions.cs diff --git a/src/Cuemon.Core/IO/StreamCopyOptions.cs b/src/Cuemon.IO/StreamCopyOptions.cs similarity index 100% rename from src/Cuemon.Core/IO/StreamCopyOptions.cs rename to src/Cuemon.IO/StreamCopyOptions.cs diff --git a/src/Cuemon.Core/IO/StreamEncodingOptions.cs b/src/Cuemon.IO/StreamEncodingOptions.cs similarity index 100% rename from src/Cuemon.Core/IO/StreamEncodingOptions.cs rename to src/Cuemon.IO/StreamEncodingOptions.cs diff --git a/src/Cuemon.Core/IO/StreamFactory.cs b/src/Cuemon.IO/StreamFactory.cs similarity index 100% rename from src/Cuemon.Core/IO/StreamFactory.cs rename to src/Cuemon.IO/StreamFactory.cs diff --git a/src/Cuemon.Core/IO/StreamOptions.cs b/src/Cuemon.IO/StreamOptions.cs similarity index 100% rename from src/Cuemon.Core/IO/StreamOptions.cs rename to src/Cuemon.IO/StreamOptions.cs diff --git a/src/Cuemon.Core/IO/StreamReaderOptions.cs b/src/Cuemon.IO/StreamReaderOptions.cs similarity index 100% rename from src/Cuemon.Core/IO/StreamReaderOptions.cs rename to src/Cuemon.IO/StreamReaderOptions.cs diff --git a/src/Cuemon.Core/IO/StreamWriterOptions.cs b/src/Cuemon.IO/StreamWriterOptions.cs similarity index 100% rename from src/Cuemon.Core/IO/StreamWriterOptions.cs rename to src/Cuemon.IO/StreamWriterOptions.cs diff --git a/src/Cuemon.Net/Cuemon.Net.csproj b/src/Cuemon.Net/Cuemon.Net.csproj index ee835941a..6c0666f7f 100644 --- a/src/Cuemon.Net/Cuemon.Net.csproj +++ b/src/Cuemon.Net/Cuemon.Net.csproj @@ -14,6 +14,7 @@ + \ No newline at end of file diff --git a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj index 9a2336890..41f707830 100644 --- a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj +++ b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj @@ -18,6 +18,7 @@ + \ No newline at end of file diff --git a/test/Cuemon.Xml.Tests/Cuemon.Xml.Tests.csproj b/test/Cuemon.Xml.Tests/Cuemon.Xml.Tests.csproj index 3e41733af..43e5e8025 100644 --- a/test/Cuemon.Xml.Tests/Cuemon.Xml.Tests.csproj +++ b/test/Cuemon.Xml.Tests/Cuemon.Xml.Tests.csproj @@ -5,6 +5,7 @@ + From 1b10df9f1a810deadd6fd49aa944a6a98f2c88be Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 00:22:46 +0200 Subject: [PATCH 095/385] Removed AsyncOptions inheritance. --- src/Cuemon.Core/DisposableOptions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Cuemon.Core/DisposableOptions.cs b/src/Cuemon.Core/DisposableOptions.cs index f8020e315..4dfba8c99 100644 --- a/src/Cuemon.Core/DisposableOptions.cs +++ b/src/Cuemon.Core/DisposableOptions.cs @@ -1,12 +1,11 @@ using System; -using Cuemon.Threading; namespace Cuemon { /// /// Configuration options for . /// - public class DisposableOptions : AsyncOptions + public class DisposableOptions { /// /// Initializes a new instance of the class. From 22c5b8da2fff065f6be815d8805075fe6d434110 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 00:48:09 +0200 Subject: [PATCH 096/385] A variant of DisposableOptions with Async support. --- src/Cuemon.Core/AsyncDisposableOptions.cs | 38 +++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/Cuemon.Core/AsyncDisposableOptions.cs diff --git a/src/Cuemon.Core/AsyncDisposableOptions.cs b/src/Cuemon.Core/AsyncDisposableOptions.cs new file mode 100644 index 000000000..13fd71a6d --- /dev/null +++ b/src/Cuemon.Core/AsyncDisposableOptions.cs @@ -0,0 +1,38 @@ +using System; +using Cuemon.Threading; + +namespace Cuemon +{ + /// + /// Configuration options for . + /// + public class AsyncDisposableOptions : AsyncOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// false + /// + /// + /// + public AsyncDisposableOptions() + { + LeaveOpen = false; + } + + /// + /// Gets or sets a value indicating whether a disposable object should bypass the mechanism for releasing unmanaged resources. Default is false. + /// + /// true if a disposable object should bypass the mechanism for releasing unmanaged resources; otherwise, false. + public bool LeaveOpen { get; set; } + } +} \ No newline at end of file From 12b893037c5af755faf1f02b3885fa269dad34a7 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 01:08:21 +0200 Subject: [PATCH 097/385] Futher reduced size of Cuemon; Cuemon.Data and Cuemon.Data.Integrity is now fully standalone libraries. Consequence changes applied. --- src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj | 3 ++- src/Cuemon.Data.Integrity/CacheValidatorOptions.cs | 3 +-- .../Integrity => Cuemon.Data.Integrity}/ChecksumBuilder.cs | 1 - .../ChecksumBuilderOptions.cs | 5 ++--- .../EntityDataIntegrityMethod.cs | 0 .../EntityDataIntegrityStrength.cs | 0 .../Extensions}/ChecksumBuilderDecoratorExtensions.cs | 0 .../Integrity => Cuemon.Data.Integrity}/IDataIntegrity.cs | 0 .../Data/Integrity => Cuemon.Data.Integrity}/IEntityData.cs | 0 .../IEntityDataIntegrity.cs | 0 .../IEntityDataTimestamp.cs | 0 .../Data => Cuemon.Data}/ConcurrentDsvDataReader.cs | 0 src/{Cuemon.Core/Data => Cuemon.Data}/DataReader.cs | 0 src/{Cuemon.Core/Data => Cuemon.Data}/DsvDataReader.cs | 0 src/{Cuemon.Core/Data => Cuemon.Data}/TokenBuilder.cs | 0 src/{Cuemon.Xml/Data => Cuemon.Data/Xml}/XmlDataReader.cs | 3 +-- test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj | 4 ++++ .../ConcurrentDsvDataReaderTest.cs | 0 .../Data => Cuemon.Data.Tests}/DsvDataReaderTest.cs | 0 19 files changed, 10 insertions(+), 9 deletions(-) rename src/{Cuemon.Core/Data/Integrity => Cuemon.Data.Integrity}/ChecksumBuilder.cs (99%) rename src/{Cuemon.Core/Data/Integrity => Cuemon.Data.Integrity}/ChecksumBuilderOptions.cs (92%) rename src/{Cuemon.Core/Data/Integrity => Cuemon.Data.Integrity}/EntityDataIntegrityMethod.cs (100%) rename src/{Cuemon.Core/Data/Integrity => Cuemon.Data.Integrity}/EntityDataIntegrityStrength.cs (100%) rename src/{Cuemon.Core/Extensions/Data/Integrity => Cuemon.Data.Integrity/Extensions}/ChecksumBuilderDecoratorExtensions.cs (100%) rename src/{Cuemon.Core/Data/Integrity => Cuemon.Data.Integrity}/IDataIntegrity.cs (100%) rename src/{Cuemon.Core/Data/Integrity => Cuemon.Data.Integrity}/IEntityData.cs (100%) rename src/{Cuemon.Core/Data/Integrity => Cuemon.Data.Integrity}/IEntityDataIntegrity.cs (100%) rename src/{Cuemon.Core/Data/Integrity => Cuemon.Data.Integrity}/IEntityDataTimestamp.cs (100%) rename src/{Cuemon.Core/Data => Cuemon.Data}/ConcurrentDsvDataReader.cs (100%) rename src/{Cuemon.Core/Data => Cuemon.Data}/DataReader.cs (100%) rename src/{Cuemon.Core/Data => Cuemon.Data}/DsvDataReader.cs (100%) rename src/{Cuemon.Core/Data => Cuemon.Data}/TokenBuilder.cs (100%) rename src/{Cuemon.Xml/Data => Cuemon.Data/Xml}/XmlDataReader.cs (99%) rename test/{Cuemon.Core.Tests/Data => Cuemon.Data.Tests}/ConcurrentDsvDataReaderTest.cs (100%) rename test/{Cuemon.Core.Tests/Data => Cuemon.Data.Tests}/DsvDataReaderTest.cs (100%) diff --git a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj index d83428ee6..68ad01a70 100644 --- a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj +++ b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj @@ -26,8 +26,9 @@ - + + \ No newline at end of file diff --git a/src/Cuemon.Data.Integrity/CacheValidatorOptions.cs b/src/Cuemon.Data.Integrity/CacheValidatorOptions.cs index 8821fd008..c23a461fe 100644 --- a/src/Cuemon.Data.Integrity/CacheValidatorOptions.cs +++ b/src/Cuemon.Data.Integrity/CacheValidatorOptions.cs @@ -1,5 +1,4 @@ -using Cuemon.Integrity; -using Cuemon.Security.Cryptography; +using Cuemon.Security.Cryptography; namespace Cuemon.Data.Integrity { diff --git a/src/Cuemon.Core/Data/Integrity/ChecksumBuilder.cs b/src/Cuemon.Data.Integrity/ChecksumBuilder.cs similarity index 99% rename from src/Cuemon.Core/Data/Integrity/ChecksumBuilder.cs rename to src/Cuemon.Data.Integrity/ChecksumBuilder.cs index 0de4f9fc3..d5fb3adf4 100644 --- a/src/Cuemon.Core/Data/Integrity/ChecksumBuilder.cs +++ b/src/Cuemon.Data.Integrity/ChecksumBuilder.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Cuemon.Integrity; using Cuemon.Security.Cryptography; namespace Cuemon.Data.Integrity diff --git a/src/Cuemon.Core/Data/Integrity/ChecksumBuilderOptions.cs b/src/Cuemon.Data.Integrity/ChecksumBuilderOptions.cs similarity index 92% rename from src/Cuemon.Core/Data/Integrity/ChecksumBuilderOptions.cs rename to src/Cuemon.Data.Integrity/ChecksumBuilderOptions.cs index eb787a2de..72ec4cf9a 100644 --- a/src/Cuemon.Core/Data/Integrity/ChecksumBuilderOptions.cs +++ b/src/Cuemon.Data.Integrity/ChecksumBuilderOptions.cs @@ -1,7 +1,6 @@ -using Cuemon.Data.Integrity; -using Cuemon.Security.Cryptography; +using Cuemon.Security.Cryptography; -namespace Cuemon.Integrity +namespace Cuemon.Data.Integrity { /// /// Configuration options for . diff --git a/src/Cuemon.Core/Data/Integrity/EntityDataIntegrityMethod.cs b/src/Cuemon.Data.Integrity/EntityDataIntegrityMethod.cs similarity index 100% rename from src/Cuemon.Core/Data/Integrity/EntityDataIntegrityMethod.cs rename to src/Cuemon.Data.Integrity/EntityDataIntegrityMethod.cs diff --git a/src/Cuemon.Core/Data/Integrity/EntityDataIntegrityStrength.cs b/src/Cuemon.Data.Integrity/EntityDataIntegrityStrength.cs similarity index 100% rename from src/Cuemon.Core/Data/Integrity/EntityDataIntegrityStrength.cs rename to src/Cuemon.Data.Integrity/EntityDataIntegrityStrength.cs diff --git a/src/Cuemon.Core/Extensions/Data/Integrity/ChecksumBuilderDecoratorExtensions.cs b/src/Cuemon.Data.Integrity/Extensions/ChecksumBuilderDecoratorExtensions.cs similarity index 100% rename from src/Cuemon.Core/Extensions/Data/Integrity/ChecksumBuilderDecoratorExtensions.cs rename to src/Cuemon.Data.Integrity/Extensions/ChecksumBuilderDecoratorExtensions.cs diff --git a/src/Cuemon.Core/Data/Integrity/IDataIntegrity.cs b/src/Cuemon.Data.Integrity/IDataIntegrity.cs similarity index 100% rename from src/Cuemon.Core/Data/Integrity/IDataIntegrity.cs rename to src/Cuemon.Data.Integrity/IDataIntegrity.cs diff --git a/src/Cuemon.Core/Data/Integrity/IEntityData.cs b/src/Cuemon.Data.Integrity/IEntityData.cs similarity index 100% rename from src/Cuemon.Core/Data/Integrity/IEntityData.cs rename to src/Cuemon.Data.Integrity/IEntityData.cs diff --git a/src/Cuemon.Core/Data/Integrity/IEntityDataIntegrity.cs b/src/Cuemon.Data.Integrity/IEntityDataIntegrity.cs similarity index 100% rename from src/Cuemon.Core/Data/Integrity/IEntityDataIntegrity.cs rename to src/Cuemon.Data.Integrity/IEntityDataIntegrity.cs diff --git a/src/Cuemon.Core/Data/Integrity/IEntityDataTimestamp.cs b/src/Cuemon.Data.Integrity/IEntityDataTimestamp.cs similarity index 100% rename from src/Cuemon.Core/Data/Integrity/IEntityDataTimestamp.cs rename to src/Cuemon.Data.Integrity/IEntityDataTimestamp.cs diff --git a/src/Cuemon.Core/Data/ConcurrentDsvDataReader.cs b/src/Cuemon.Data/ConcurrentDsvDataReader.cs similarity index 100% rename from src/Cuemon.Core/Data/ConcurrentDsvDataReader.cs rename to src/Cuemon.Data/ConcurrentDsvDataReader.cs diff --git a/src/Cuemon.Core/Data/DataReader.cs b/src/Cuemon.Data/DataReader.cs similarity index 100% rename from src/Cuemon.Core/Data/DataReader.cs rename to src/Cuemon.Data/DataReader.cs diff --git a/src/Cuemon.Core/Data/DsvDataReader.cs b/src/Cuemon.Data/DsvDataReader.cs similarity index 100% rename from src/Cuemon.Core/Data/DsvDataReader.cs rename to src/Cuemon.Data/DsvDataReader.cs diff --git a/src/Cuemon.Core/Data/TokenBuilder.cs b/src/Cuemon.Data/TokenBuilder.cs similarity index 100% rename from src/Cuemon.Core/Data/TokenBuilder.cs rename to src/Cuemon.Data/TokenBuilder.cs diff --git a/src/Cuemon.Xml/Data/XmlDataReader.cs b/src/Cuemon.Data/Xml/XmlDataReader.cs similarity index 99% rename from src/Cuemon.Xml/Data/XmlDataReader.cs rename to src/Cuemon.Data/Xml/XmlDataReader.cs index 576cf1910..6ba0d87c8 100644 --- a/src/Cuemon.Xml/Data/XmlDataReader.cs +++ b/src/Cuemon.Data/Xml/XmlDataReader.cs @@ -2,10 +2,9 @@ using System.Collections.Specialized; using System.Globalization; using System.Xml; -using Cuemon.Data; using Cuemon.Text; -namespace Cuemon.Xml.Data +namespace Cuemon.Data.Xml { /// /// Provides a way of reading a forward-only stream of rows from an XML based data source. This class cannot be inherited. diff --git a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj index 41f707830..31b346e5d 100644 --- a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj +++ b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj @@ -21,4 +21,8 @@ + + + + \ No newline at end of file diff --git a/test/Cuemon.Core.Tests/Data/ConcurrentDsvDataReaderTest.cs b/test/Cuemon.Data.Tests/ConcurrentDsvDataReaderTest.cs similarity index 100% rename from test/Cuemon.Core.Tests/Data/ConcurrentDsvDataReaderTest.cs rename to test/Cuemon.Data.Tests/ConcurrentDsvDataReaderTest.cs diff --git a/test/Cuemon.Core.Tests/Data/DsvDataReaderTest.cs b/test/Cuemon.Data.Tests/DsvDataReaderTest.cs similarity index 100% rename from test/Cuemon.Core.Tests/Data/DsvDataReaderTest.cs rename to test/Cuemon.Data.Tests/DsvDataReaderTest.cs From 20a15a93e8df6daf22bb3cb2bfe2565185c4cf2a Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 01:30:57 +0200 Subject: [PATCH 098/385] Furhter refactoring away from Core. --- .../Extensions/StringDecoratorExtensions.cs | 10 ++++++---- src/Cuemon.Core/Text/EncodingOptions.cs | 3 +-- src/Cuemon.Extensions.IO/StringExtensions.cs | 6 ++++-- .../AsyncDisposableOptions.cs | 2 +- src/Cuemon.IO/Cuemon.IO.csproj | 1 + .../Threading => Cuemon.Threading}/AsyncOptions.cs | 0 .../Threading => Cuemon.Threading}/TimerFactory.cs | 0 7 files changed, 13 insertions(+), 9 deletions(-) rename src/{Cuemon.Core => Cuemon.IO}/AsyncDisposableOptions.cs (98%) rename src/{Cuemon.Core/Threading => Cuemon.Threading}/AsyncOptions.cs (100%) rename src/{Cuemon.Core/Threading => Cuemon.Threading}/TimerFactory.cs (100%) diff --git a/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs b/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs index 8d23a921f..6616be6e7 100644 --- a/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs +++ b/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.IO; using System.Text; +using System.Threading; using System.Threading.Tasks; using Cuemon.Text; @@ -152,6 +153,7 @@ public static Stream ToStream(this IDecorator decorator, Action of the specified to a . /// /// The to extend. + /// The token to monitor for cancellation requests. The default value is . /// The which may be configured. /// A task that represents the asynchronous operation. The task result contains a containing the result of the enclosed of the specified . /// will be initialized with and . @@ -161,17 +163,17 @@ public static Stream ToStream(this IDecorator decorator, Action /// was initialized with an invalid . /// - public static Task ToStreamAsync(this IDecorator decorator, Action setup = null) + public static Task ToStreamAsync(this IDecorator decorator, CancellationToken ct = default, Action setup = null) { Validator.ThrowIfNull(decorator, nameof(decorator)); var options = Patterns.Configure(setup); - return Disposable.SafeInvokeAsync(() => new MemoryStream(), async (ms, ct) => + return Disposable.SafeInvokeAsync(() => new MemoryStream(), async (ms, token) => { var bytes = Convertible.GetBytes(decorator.Inner, setup); - await ms.WriteAsync(bytes, 0, bytes.Length, ct).ConfigureAwait(false); + await ms.WriteAsync(bytes, 0, bytes.Length, token).ConfigureAwait(false); ms.Position = 0; return ms; - }, options.CancellationToken); + }, ct); } /// diff --git a/src/Cuemon.Core/Text/EncodingOptions.cs b/src/Cuemon.Core/Text/EncodingOptions.cs index 7cc55b22e..af3d9ff36 100644 --- a/src/Cuemon.Core/Text/EncodingOptions.cs +++ b/src/Cuemon.Core/Text/EncodingOptions.cs @@ -1,13 +1,12 @@ using System; using System.Text; -using Cuemon.Threading; namespace Cuemon.Text { /// /// Configuration options related to . /// - public class EncodingOptions : AsyncOptions, IEncodingOptions + public class EncodingOptions : IEncodingOptions { private Encoding _encoding; diff --git a/src/Cuemon.Extensions.IO/StringExtensions.cs b/src/Cuemon.Extensions.IO/StringExtensions.cs index 98ef14c8b..59ac845fd 100644 --- a/src/Cuemon.Extensions.IO/StringExtensions.cs +++ b/src/Cuemon.Extensions.IO/StringExtensions.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using System.IO; +using System.Threading; using System.Threading.Tasks; using Cuemon.Text; @@ -33,6 +34,7 @@ public static Stream ToStream(this string value, Action setup = /// Converts the specified to a . /// /// The to extend. + /// The token to monitor for cancellation requests. The default value is . /// The which may be configured. /// A task that represents the asynchronous operation. The task result contains a containing the result of the specified . /// will be initialized with and . @@ -42,9 +44,9 @@ public static Stream ToStream(this string value, Action setup = /// /// was initialized with an invalid . /// - public static Task ToStreamAsync(this string value, Action setup = null) + public static Task ToStreamAsync(this string value, CancellationToken ct = default, Action setup = null) { - return Decorator.Enclose(value).ToStreamAsync(setup); + return Decorator.Enclose(value).ToStreamAsync(ct, setup); } /// diff --git a/src/Cuemon.Core/AsyncDisposableOptions.cs b/src/Cuemon.IO/AsyncDisposableOptions.cs similarity index 98% rename from src/Cuemon.Core/AsyncDisposableOptions.cs rename to src/Cuemon.IO/AsyncDisposableOptions.cs index 13fd71a6d..34f2d1ecf 100644 --- a/src/Cuemon.Core/AsyncDisposableOptions.cs +++ b/src/Cuemon.IO/AsyncDisposableOptions.cs @@ -1,7 +1,7 @@ using System; using Cuemon.Threading; -namespace Cuemon +namespace Cuemon.IO { /// /// Configuration options for . diff --git a/src/Cuemon.IO/Cuemon.IO.csproj b/src/Cuemon.IO/Cuemon.IO.csproj index b33752526..e3b2d643b 100644 --- a/src/Cuemon.IO/Cuemon.IO.csproj +++ b/src/Cuemon.IO/Cuemon.IO.csproj @@ -14,6 +14,7 @@ + \ No newline at end of file diff --git a/src/Cuemon.Core/Threading/AsyncOptions.cs b/src/Cuemon.Threading/AsyncOptions.cs similarity index 100% rename from src/Cuemon.Core/Threading/AsyncOptions.cs rename to src/Cuemon.Threading/AsyncOptions.cs diff --git a/src/Cuemon.Core/Threading/TimerFactory.cs b/src/Cuemon.Threading/TimerFactory.cs similarity index 100% rename from src/Cuemon.Core/Threading/TimerFactory.cs rename to src/Cuemon.Threading/TimerFactory.cs From dc3a5a57e3a1ff00c48718cecd743c8f73d293e9 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 02:37:23 +0200 Subject: [PATCH 099/385] Fixed test after refactoring. --- test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj | 8 -------- .../Reflection/AssemblyDecoratorExtensionsTest.cs | 4 ++-- .../Assets/DsvDataReaderTest_Wiki.csv | 0 test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj | 8 ++++++++ 4 files changed, 10 insertions(+), 10 deletions(-) rename test/{Cuemon.Core.Tests => Cuemon.Data.Tests}/Assets/DsvDataReaderTest_Wiki.csv (100%) diff --git a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj index 31b346e5d..381ebe1f1 100644 --- a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj +++ b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj @@ -4,14 +4,6 @@ Cuemon - - - - - - - - diff --git a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs index 672ebd186..226b3899c 100644 --- a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs @@ -36,8 +36,8 @@ public void GetTypes_ShouldReturnAllTypesFromCuemonCore() var disposableTypesCount = Decorator.Enclose(disposableTypes).Inner.Count(); var configurationTypesCount = Decorator.Enclose(configurationTypes).Inner.Count(); - Assert.InRange(allTypesCount, 525, 530); // range because of tooling on CI adding dynamic types - Assert.Equal(7, disposableTypesCount); + Assert.InRange(allTypesCount, 485, 490); // range because of tooling on CI adding dynamic types + Assert.Equal(4, disposableTypesCount); Assert.Equal(2, configurationTypesCount); } diff --git a/test/Cuemon.Core.Tests/Assets/DsvDataReaderTest_Wiki.csv b/test/Cuemon.Data.Tests/Assets/DsvDataReaderTest_Wiki.csv similarity index 100% rename from test/Cuemon.Core.Tests/Assets/DsvDataReaderTest_Wiki.csv rename to test/Cuemon.Data.Tests/Assets/DsvDataReaderTest_Wiki.csv diff --git a/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj b/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj index ec7ed8606..982a39029 100644 --- a/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj +++ b/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj @@ -4,6 +4,14 @@ Cuemon.Data + + + + + + + + From 5b83ba6fe91289aab8d1ef44d5b802cd5c715250 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 02:47:05 +0200 Subject: [PATCH 100/385] Changed to auto-property. --- .../Security/Cryptography/CyclicRedundancyCheck.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck.cs b/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck.cs index f8a267a12..6361e0c74 100644 --- a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck.cs +++ b/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck.cs @@ -10,9 +10,6 @@ namespace Cuemon.Security.Cryptography public abstract class CyclicRedundancyCheck : Hash { private readonly Lazy> _lazyLookup; - private readonly ulong _initialValue; - private readonly ulong _finalXor; - /// /// Initializes a new instance of the class. @@ -24,8 +21,8 @@ public abstract class CyclicRedundancyCheck : Hash protected CyclicRedundancyCheck(ulong polynomial, ulong initialValue, ulong finalXor, Action setup) : base(setup) { _lazyLookup = new Lazy>(() => PolynomialTableInitializerCore(polynomial)); - _initialValue = initialValue; - _finalXor = finalXor; + InitialValue = initialValue; + FinalXor = finalXor; } private List PolynomialTableInitializerCore(ulong polynomial) @@ -70,12 +67,12 @@ private List PolynomialTableInitializerCore(ulong polynomial) /// Gets the CRC initial value of the register. /// /// The CRC initial value of the register. - public ulong InitialValue => _initialValue; + public ulong InitialValue { get; } /// /// Gets the CRC final value that is XORed to the final register value. /// /// The CRC final value that is XORed to the final register value. - public ulong FinalXor => _finalXor; + public ulong FinalXor { get; } } } \ No newline at end of file From 6868b9921bebe0915e3b9f7d227994dd9603872c Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 16:58:25 +0200 Subject: [PATCH 101/385] Refactored Cuemon.Security.Cryptography to its own assembly. Adjusted accordingly. --- .../Cuemon.AspNetCore.Authentication.csproj | 1 + .../DigestAccessAuthenticationOptions.cs | 6 +- .../DigestAccessAuthenticationParameters.cs | 4 +- .../DigestAuthenticationUtility.cs | 26 +-- .../HmacAuthenticationMiddleware.cs | 2 +- .../HmacAuthenticationOptions.cs | 2 +- src/Cuemon.Core/Generate.cs | 2 +- .../CyclicRedundancyCheck.cs | 2 +- .../CyclicRedundancyCheck32.cs | 2 +- .../CyclicRedundancyCheck64.cs | 2 +- .../CyclicRedundancyCheckAlgorithm.cs | 2 +- .../CyclicRedundancyCheckOptions.cs | 2 +- .../{Cryptography => }/FowlerNollVo1024.cs | 2 +- .../{Cryptography => }/FowlerNollVo128.cs | 2 +- .../{Cryptography => }/FowlerNollVo256.cs | 2 +- .../{Cryptography => }/FowlerNollVo32.cs | 2 +- .../{Cryptography => }/FowlerNollVo512.cs | 2 +- .../{Cryptography => }/FowlerNollVo64.cs | 2 +- .../FowlerNollVoAlgorithm.cs | 2 +- .../{Cryptography => }/FowlerNollVoHash.cs | 2 +- .../{Cryptography => }/FowlerNollVoOptions.cs | 2 +- .../Security/{Cryptography => }/Hash.cs | 2 +- .../{Cryptography => }/HashFactory.cs | 177 +----------------- .../Security/{Cryptography => }/HashResult.cs | 4 +- .../Security/{Cryptography => }/IHash.cs | 4 +- .../{Cryptography => }/NonCryptoAlgorithm.cs | 2 +- .../Cuemon.Extensions.Net.csproj | 1 + .../Security/StringExtensions.cs | 4 +- .../AesCryptor.cs | 0 .../AesCryptorOptions.cs | 0 .../AesKeyOptions.cs | 0 .../AesSize.cs | 0 .../Cuemon.Security.Cryptography.csproj | 19 ++ .../HmacMessageDigest5.cs | 0 .../HmacSecureHashAlgorithm1.cs | 0 .../HmacSecureHashAlgorithm256.cs | 0 .../HmacSecureHashAlgorithm384.cs | 0 .../HmacSecureHashAlgorithm512.cs | 0 .../KeyedCryptoAlgorithm.cs | 0 .../KeyedCryptoHash.cs | 0 .../KeyedHashFactory.cs | 112 +++++++++++ .../MessageDigest5.cs | 0 .../Properties/AssemblyInfo.cs | 4 + .../SecureHashAlgorithm1.cs | 0 .../SecureHashAlgorithm256.cs | 0 .../SecureHashAlgorithm384.cs | 0 .../SecureHashAlgorithm512.cs | 0 .../UnkeyedCryptoAlgorithm.cs} | 2 +- .../UnkeyedCryptoHash.cs | 0 .../UnkeyedHashFactory.cs | 83 ++++++++ 50 files changed, 265 insertions(+), 220 deletions(-) rename src/Cuemon.Core/Security/{Cryptography => }/CyclicRedundancyCheck.cs (98%) rename src/Cuemon.Core/Security/{Cryptography => }/CyclicRedundancyCheck32.cs (98%) rename src/Cuemon.Core/Security/{Cryptography => }/CyclicRedundancyCheck64.cs (98%) rename src/Cuemon.Core/Security/{Cryptography => }/CyclicRedundancyCheckAlgorithm.cs (97%) rename src/Cuemon.Core/Security/{Cryptography => }/CyclicRedundancyCheckOptions.cs (98%) rename src/Cuemon.Core/Security/{Cryptography => }/FowlerNollVo1024.cs (97%) rename src/Cuemon.Core/Security/{Cryptography => }/FowlerNollVo128.cs (95%) rename src/Cuemon.Core/Security/{Cryptography => }/FowlerNollVo256.cs (95%) rename src/Cuemon.Core/Security/{Cryptography => }/FowlerNollVo32.cs (95%) rename src/Cuemon.Core/Security/{Cryptography => }/FowlerNollVo512.cs (96%) rename src/Cuemon.Core/Security/{Cryptography => }/FowlerNollVo64.cs (95%) rename src/Cuemon.Core/Security/{Cryptography => }/FowlerNollVoAlgorithm.cs (89%) rename src/Cuemon.Core/Security/{Cryptography => }/FowlerNollVoHash.cs (98%) rename src/Cuemon.Core/Security/{Cryptography => }/FowlerNollVoOptions.cs (97%) rename src/Cuemon.Core/Security/{Cryptography => }/Hash.cs (99%) rename src/Cuemon.Core/Security/{Cryptography => }/HashFactory.cs (60%) rename src/Cuemon.Core/Security/{Cryptography => }/HashResult.cs (98%) rename src/Cuemon.Core/Security/{Cryptography => }/IHash.cs (88%) rename src/Cuemon.Core/Security/{Cryptography => }/NonCryptoAlgorithm.cs (95%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/AesCryptor.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/AesCryptorOptions.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/AesKeyOptions.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/AesSize.cs (100%) create mode 100644 src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/HmacMessageDigest5.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/HmacSecureHashAlgorithm1.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/HmacSecureHashAlgorithm256.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/HmacSecureHashAlgorithm384.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/HmacSecureHashAlgorithm512.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/KeyedCryptoAlgorithm.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/KeyedCryptoHash.cs (100%) create mode 100644 src/Cuemon.Security.Cryptography/KeyedHashFactory.cs rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/MessageDigest5.cs (100%) create mode 100644 src/Cuemon.Security.Cryptography/Properties/AssemblyInfo.cs rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/SecureHashAlgorithm1.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/SecureHashAlgorithm256.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/SecureHashAlgorithm384.cs (100%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/SecureHashAlgorithm512.cs (100%) rename src/{Cuemon.Core/Security/Cryptography/CryptoAlgorithm.cs => Cuemon.Security.Cryptography/UnkeyedCryptoAlgorithm.cs} (95%) rename src/{Cuemon.Core/Security/Cryptography => Cuemon.Security.Cryptography}/UnkeyedCryptoHash.cs (100%) create mode 100644 src/Cuemon.Security.Cryptography/UnkeyedHashFactory.cs diff --git a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj index e1dc8a589..f814a7cc8 100644 --- a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj +++ b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj @@ -14,6 +14,7 @@ + \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs index c7cb36ee2..3b3bf66c8 100644 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs +++ b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs @@ -14,7 +14,7 @@ public sealed class DigestAccessAuthenticationOptions : AuthenticationOptions /// public DigestAccessAuthenticationOptions() { - Algorithm = CryptoAlgorithm.Md5; + Algorithm = UnkeyedCryptoAlgorithm.Md5; OpaqueGenerator = DigestAuthenticationUtility.DefaultOpaqueGenerator; NonceExpiredParser = DigestAuthenticationUtility.DefaultNonceExpiredParser; NonceGenerator = DigestAuthenticationUtility.DefaultNonceGenerator; @@ -40,10 +40,10 @@ public DigestAccessAuthenticationOptions() public Func DigestAccessSigner { get; set; } /// - /// Gets or sets the algorithm of the HTTP Digest Access Authentication. Default is . + /// Gets or sets the algorithm of the HTTP Digest Access Authentication. Default is . /// /// The algorithm of the HTTP Digest Access Authentication. - public CryptoAlgorithm Algorithm { get; set; } + public UnkeyedCryptoAlgorithm Algorithm { get; set; } /// /// Gets the realm that defines the protection space. diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs index 534429b33..9915e7c14 100644 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs +++ b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs @@ -15,7 +15,7 @@ public class DigestAccessAuthenticationParameters /// The HTTP method to include in the HA2 computed value. /// The password to include in the HA1 computed value. /// The algorithm to use when computing the HA1-, HA2-, and response hash values. - internal DigestAccessAuthenticationParameters(ImmutableDictionary credentials, string httpMethod, string password, CryptoAlgorithm algorithm) + internal DigestAccessAuthenticationParameters(ImmutableDictionary credentials, string httpMethod, string password, UnkeyedCryptoAlgorithm algorithm) { Credentials = credentials; HttpMethod = httpMethod; @@ -45,6 +45,6 @@ internal DigestAccessAuthenticationParameters(ImmutableDictionary /// The algorithm to use when computing the HA1-, HA2-, and response hash values. - public CryptoAlgorithm Algorithm { get; } + public UnkeyedCryptoAlgorithm Algorithm { get; } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAuthenticationUtility.cs b/src/Cuemon.AspNetCore.Authentication/DigestAuthenticationUtility.cs index a80ddfc46..097e91de3 100644 --- a/src/Cuemon.AspNetCore.Authentication/DigestAuthenticationUtility.cs +++ b/src/Cuemon.AspNetCore.Authentication/DigestAuthenticationUtility.cs @@ -70,49 +70,49 @@ public static class DigestAuthenticationUtility public const string CredentialAlgorithm = "algorithm"; /// - /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA1. + /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA1. /// /// The credentials of the HA1 computed value (, ). /// The password to include in the HA1 computed value. /// The algorithm to use when computing the HA1 value. /// A in the format of H('[CredentialUserName]:[CredentialRealm]:'). - public static string ComputeHash1(IDictionary credentials, string password, CryptoAlgorithm algorithm) + public static string ComputeHash1(IDictionary credentials, string password, UnkeyedCryptoAlgorithm algorithm) { ValidateCredentials(credentials, CredentialUserName, CredentialRealm); - return HashFactory.CreateCrypto(algorithm).ComputeHash(string.Format(CultureInfo.InvariantCulture, "{0}:{1}:{2}", credentials[CredentialUserName], credentials[CredentialRealm], password), o => + return UnkeyedHashFactory.CreateCrypto(algorithm).ComputeHash(string.Format(CultureInfo.InvariantCulture, "{0}:{1}:{2}", credentials[CredentialUserName], credentials[CredentialRealm], password), o => { o.Encoding = Encoding.UTF8; }).ToHexadecimalString(); } /// - /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA2. + /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA2. /// /// The credentials of the HA2 computed value (). /// The HTTP method to include in the HA2 computed value. /// The algorithm to use when computing the HA2 value. /// A in the format of H(':[CredentialDigestUri]'). - public static string ComputeHash2(IDictionary credentials, string httpMethod, CryptoAlgorithm algorithm) + public static string ComputeHash2(IDictionary credentials, string httpMethod, UnkeyedCryptoAlgorithm algorithm) { ValidateCredentials(credentials, CredentialDigestUri); - return HashFactory.CreateCrypto(algorithm).ComputeHash(string.Format(CultureInfo.InvariantCulture, "{0}:{1}", httpMethod, credentials[CredentialDigestUri]), o => + return UnkeyedHashFactory.CreateCrypto(algorithm).ComputeHash(string.Format(CultureInfo.InvariantCulture, "{0}:{1}", httpMethod, credentials[CredentialDigestUri]), o => { o.Encoding = Encoding.UTF8; }).ToHexadecimalString(); } /// - /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication RESPONSE. + /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication RESPONSE. /// /// The credentials of the RESPONSE computed value (, , , ). /// The HA1 to include in the RESPONSE computed value. /// The HA2 to include in the RESPONSE computed value. /// The algorithm to use when computing the RESPONSE value. /// A in the format of H(':[CredentialNonce]:[CredentialNonceCount]:[CredentialClientNonce]:[CredentialQualityOfProtection]:'). - public static byte[] ComputeResponse(IDictionary credentials, string hash1, string hash2, CryptoAlgorithm algorithm) + public static byte[] ComputeResponse(IDictionary credentials, string hash1, string hash2, UnkeyedCryptoAlgorithm algorithm) { ValidateCredentials(credentials, CredentialNonce, CredentialNonceCount, CredentialClientNonce, CredentialQualityOfProtection); - return HashFactory.CreateCrypto(algorithm).ComputeHash(FormattableString.Invariant($"{hash1}:{credentials[CredentialNonce]}:{credentials[CredentialNonceCount]}:{credentials[CredentialClientNonce]}:{credentials[CredentialQualityOfProtection]}:{hash2}"), o => + return UnkeyedHashFactory.CreateCrypto(algorithm).ComputeHash(FormattableString.Invariant($"{hash1}:{credentials[CredentialNonce]}:{credentials[CredentialNonceCount]}:{credentials[CredentialClientNonce]}:{credentials[CredentialQualityOfProtection]}:{hash2}"), o => { o.Encoding = Encoding.UTF8; }).GetBytes(); @@ -175,13 +175,13 @@ public static string DefaultOpaqueGenerator() /// /// The algorithm to convert. /// A string containing either MD5, SHA-256 or SHA-512-256. - public static string ParseAlgorithm(CryptoAlgorithm algorithm) + public static string ParseAlgorithm(UnkeyedCryptoAlgorithm algorithm) { switch (algorithm) { - case CryptoAlgorithm.Sha256: + case UnkeyedCryptoAlgorithm.Sha256: return "SHA-256"; - case CryptoAlgorithm.Sha512: + case UnkeyedCryptoAlgorithm.Sha512: return "SHA-512-256"; default: return "MD5"; @@ -199,7 +199,7 @@ private static void ValidateCredentials(IDictionary credentials, private static string ComputeNonceHash(DateTime timeStamp, string entityTag, byte[] privateKey) { - return HashFactory.CreateCryptoSha256().ComputeHash(timeStamp, entityTag, Convert.ToBase64String(privateKey)).ToHexadecimalString(); + return UnkeyedHashFactory.CreateCryptoSha256().ComputeHash(timeStamp, entityTag, Convert.ToBase64String(privateKey)).ToHexadecimalString(); } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs index 47ef8603d..705382655 100644 --- a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs @@ -55,7 +55,7 @@ private bool TryAuthenticate(HttpContext context, Template crede { if (Options.Authenticator == null) { throw new InvalidOperationException(FormattableString.Invariant($"The {nameof(Options.Authenticator)} cannot be null.")); } var requestBodyMd5 = context.Request.Headers[HeaderNames.ContentMD5].FirstOrDefault()?.ToLowerInvariant(); - if (!string.IsNullOrWhiteSpace(requestBodyMd5) && !HashFactory.CreateCrypto(CryptoAlgorithm.Md5).ComputeHash(context.Request.Body).ToHexadecimalString().Equals(requestBodyMd5, StringComparison.Ordinal)) + if (!string.IsNullOrWhiteSpace(requestBodyMd5) && !UnkeyedHashFactory.CreateCrypto(UnkeyedCryptoAlgorithm.Md5).ComputeHash(context.Request.Body).ToHexadecimalString().Equals(requestBodyMd5, StringComparison.Ordinal)) { result = null; return false; diff --git a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationOptions.cs index 9399a8564..21caa9038 100644 --- a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationOptions.cs +++ b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationOptions.cs @@ -22,7 +22,7 @@ public HmacAuthenticationOptions() AuthenticationScheme = "HMAC"; Algorithm = KeyedCryptoAlgorithm.HmacSha1; MessageDescriptor = context => FormattableString.Invariant($"{context.Request.Method}:{context.Request.GetDisplayUrl()}:{context.Request.Headers[HeaderNames.ContentMD5].FirstOrDefault()}:{context.Request.Headers[HeaderNames.ContentType].FirstOrDefault()}:{context.Request.Headers[HeaderNames.Date].FirstOrDefault()}:{context.Request.Headers[HeaderNames.UserAgent].FirstOrDefault()}"); - HmacSigner = parameters => HashFactory.CreateHmacCrypto(parameters.PrivateKey, parameters.Algorithm).ComputeHash(parameters.Message, o => + HmacSigner = parameters => KeyedHashFactory.CreateHmacCrypto(parameters.PrivateKey, parameters.Algorithm).ComputeHash(parameters.Message, o => { o.Encoding = Encoding.UTF8; }).GetBytes(); diff --git a/src/Cuemon.Core/Generate.cs b/src/Cuemon.Core/Generate.cs index 8cb92d757..a8ec7a7c0 100644 --- a/src/Cuemon.Core/Generate.cs +++ b/src/Cuemon.Core/Generate.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using Cuemon.Collections.Generic; using Cuemon.Reflection; -using Cuemon.Security.Cryptography; +using Cuemon.Security; namespace Cuemon { diff --git a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck.cs b/src/Cuemon.Core/Security/CyclicRedundancyCheck.cs similarity index 98% rename from src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck.cs rename to src/Cuemon.Core/Security/CyclicRedundancyCheck.cs index 6361e0c74..fa952a170 100644 --- a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck.cs +++ b/src/Cuemon.Core/Security/CyclicRedundancyCheck.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Represents the base class from which all implementations of the CRC (Cyclic Redundancy Check) checksum algorithm must derive. diff --git a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck32.cs b/src/Cuemon.Core/Security/CyclicRedundancyCheck32.cs similarity index 98% rename from src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck32.cs rename to src/Cuemon.Core/Security/CyclicRedundancyCheck32.cs index 29378b6ca..f0e31694d 100644 --- a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck32.cs +++ b/src/Cuemon.Core/Security/CyclicRedundancyCheck32.cs @@ -1,6 +1,6 @@ using System; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Provides a CRC-32 implementation of the CRC (Cyclic Redundancy Check) checksum algorithm for 32-bit hash values. This class cannot be inherited. diff --git a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck64.cs b/src/Cuemon.Core/Security/CyclicRedundancyCheck64.cs similarity index 98% rename from src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck64.cs rename to src/Cuemon.Core/Security/CyclicRedundancyCheck64.cs index 66825b874..c71b4e327 100644 --- a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheck64.cs +++ b/src/Cuemon.Core/Security/CyclicRedundancyCheck64.cs @@ -1,6 +1,6 @@ using System; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Provides a CRC-64 implementation of the CRC (Cyclic Redundancy Check) checksum algorithm for 64-bit hash values. This class cannot be inherited. diff --git a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheckAlgorithm.cs b/src/Cuemon.Core/Security/CyclicRedundancyCheckAlgorithm.cs similarity index 97% rename from src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheckAlgorithm.cs rename to src/Cuemon.Core/Security/CyclicRedundancyCheckAlgorithm.cs index 3f4b210f1..950f8d747 100644 --- a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheckAlgorithm.cs +++ b/src/Cuemon.Core/Security/CyclicRedundancyCheckAlgorithm.cs @@ -1,4 +1,4 @@ -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Different models of the CRC algorithm family. diff --git a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheckOptions.cs b/src/Cuemon.Core/Security/CyclicRedundancyCheckOptions.cs similarity index 98% rename from src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheckOptions.cs rename to src/Cuemon.Core/Security/CyclicRedundancyCheckOptions.cs index 288a5b4f2..9b04c4837 100644 --- a/src/Cuemon.Core/Security/Cryptography/CyclicRedundancyCheckOptions.cs +++ b/src/Cuemon.Core/Security/CyclicRedundancyCheckOptions.cs @@ -1,4 +1,4 @@ -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Configuration options for . diff --git a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo1024.cs b/src/Cuemon.Core/Security/FowlerNollVo1024.cs similarity index 97% rename from src/Cuemon.Core/Security/Cryptography/FowlerNollVo1024.cs rename to src/Cuemon.Core/Security/FowlerNollVo1024.cs index 8a6546a29..031d979ce 100644 --- a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo1024.cs +++ b/src/Cuemon.Core/Security/FowlerNollVo1024.cs @@ -1,7 +1,7 @@ using System; using System.Numerics; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Provides an implementation of the FVN (Fowler–Noll–Vo) non-cryptographic hashing algorithm for 1024-bit hash values. This class cannot be inherited. diff --git a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo128.cs b/src/Cuemon.Core/Security/FowlerNollVo128.cs similarity index 95% rename from src/Cuemon.Core/Security/Cryptography/FowlerNollVo128.cs rename to src/Cuemon.Core/Security/FowlerNollVo128.cs index 2ceb65ef7..986957c52 100644 --- a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo128.cs +++ b/src/Cuemon.Core/Security/FowlerNollVo128.cs @@ -1,7 +1,7 @@ using System; using System.Numerics; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Provides an implementation of the FVN (Fowler–Noll–Vo) non-cryptographic hashing algorithm for 128-bit hash values. This class cannot be inherited. diff --git a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo256.cs b/src/Cuemon.Core/Security/FowlerNollVo256.cs similarity index 95% rename from src/Cuemon.Core/Security/Cryptography/FowlerNollVo256.cs rename to src/Cuemon.Core/Security/FowlerNollVo256.cs index 62a9f6407..a4bcc0be8 100644 --- a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo256.cs +++ b/src/Cuemon.Core/Security/FowlerNollVo256.cs @@ -1,7 +1,7 @@ using System; using System.Numerics; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Provides an implementation of the FVN (Fowler–Noll–Vo) non-cryptographic hashing algorithm for 256-bit hash values. This class cannot be inherited. diff --git a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo32.cs b/src/Cuemon.Core/Security/FowlerNollVo32.cs similarity index 95% rename from src/Cuemon.Core/Security/Cryptography/FowlerNollVo32.cs rename to src/Cuemon.Core/Security/FowlerNollVo32.cs index 69e448af8..2e75d80d9 100644 --- a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo32.cs +++ b/src/Cuemon.Core/Security/FowlerNollVo32.cs @@ -1,7 +1,7 @@ using System; using System.Numerics; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Provides an implementation of the FVN (Fowler–Noll–Vo) non-cryptographic hashing algorithm for 32-bit hash values. This class cannot be inherited. diff --git a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo512.cs b/src/Cuemon.Core/Security/FowlerNollVo512.cs similarity index 96% rename from src/Cuemon.Core/Security/Cryptography/FowlerNollVo512.cs rename to src/Cuemon.Core/Security/FowlerNollVo512.cs index 57e73a75e..d6be422d3 100644 --- a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo512.cs +++ b/src/Cuemon.Core/Security/FowlerNollVo512.cs @@ -1,7 +1,7 @@ using System; using System.Numerics; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Provides an implementation of the FVN (Fowler–Noll–Vo) non-cryptographic hashing algorithm for 512-bit hash values. This class cannot be inherited. diff --git a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo64.cs b/src/Cuemon.Core/Security/FowlerNollVo64.cs similarity index 95% rename from src/Cuemon.Core/Security/Cryptography/FowlerNollVo64.cs rename to src/Cuemon.Core/Security/FowlerNollVo64.cs index cbc648267..badb8624c 100644 --- a/src/Cuemon.Core/Security/Cryptography/FowlerNollVo64.cs +++ b/src/Cuemon.Core/Security/FowlerNollVo64.cs @@ -1,7 +1,7 @@ using System; using System.Numerics; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Provides an implementation of the FVN (Fowler–Noll–Vo) non-cryptographic hashing algorithm for 64-bit hash values. This class cannot be inherited. diff --git a/src/Cuemon.Core/Security/Cryptography/FowlerNollVoAlgorithm.cs b/src/Cuemon.Core/Security/FowlerNollVoAlgorithm.cs similarity index 89% rename from src/Cuemon.Core/Security/Cryptography/FowlerNollVoAlgorithm.cs rename to src/Cuemon.Core/Security/FowlerNollVoAlgorithm.cs index aaab74f95..a3dec618e 100644 --- a/src/Cuemon.Core/Security/Cryptography/FowlerNollVoAlgorithm.cs +++ b/src/Cuemon.Core/Security/FowlerNollVoAlgorithm.cs @@ -1,4 +1,4 @@ -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Defines the algorithms of the Fowler-Noll-Vo hash function. diff --git a/src/Cuemon.Core/Security/Cryptography/FowlerNollVoHash.cs b/src/Cuemon.Core/Security/FowlerNollVoHash.cs similarity index 98% rename from src/Cuemon.Core/Security/Cryptography/FowlerNollVoHash.cs rename to src/Cuemon.Core/Security/FowlerNollVoHash.cs index 5dbe8ecf2..37c5f493c 100644 --- a/src/Cuemon.Core/Security/Cryptography/FowlerNollVoHash.cs +++ b/src/Cuemon.Core/Security/FowlerNollVoHash.cs @@ -1,7 +1,7 @@ using System; using System.Numerics; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Represents the base class from which all implementations of the Fowler–Noll–Vo non-cryptographic hashing algorithm must derive. diff --git a/src/Cuemon.Core/Security/Cryptography/FowlerNollVoOptions.cs b/src/Cuemon.Core/Security/FowlerNollVoOptions.cs similarity index 97% rename from src/Cuemon.Core/Security/Cryptography/FowlerNollVoOptions.cs rename to src/Cuemon.Core/Security/FowlerNollVoOptions.cs index ade6c0ac2..fd9da5490 100644 --- a/src/Cuemon.Core/Security/Cryptography/FowlerNollVoOptions.cs +++ b/src/Cuemon.Core/Security/FowlerNollVoOptions.cs @@ -1,4 +1,4 @@ -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Configuration options for . diff --git a/src/Cuemon.Core/Security/Cryptography/Hash.cs b/src/Cuemon.Core/Security/Hash.cs similarity index 99% rename from src/Cuemon.Core/Security/Cryptography/Hash.cs rename to src/Cuemon.Core/Security/Hash.cs index 4cabf26e5..f63624b0a 100644 --- a/src/Cuemon.Core/Security/Cryptography/Hash.cs +++ b/src/Cuemon.Core/Security/Hash.cs @@ -6,7 +6,7 @@ using Cuemon.IO; using Cuemon.Text; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Represents the base class from which all implementations of hash algorithms and checksums should derive. diff --git a/src/Cuemon.Core/Security/Cryptography/HashFactory.cs b/src/Cuemon.Core/Security/HashFactory.cs similarity index 60% rename from src/Cuemon.Core/Security/Cryptography/HashFactory.cs rename to src/Cuemon.Core/Security/HashFactory.cs index ef8bc2ec0..e109e8b67 100644 --- a/src/Cuemon.Core/Security/Cryptography/HashFactory.cs +++ b/src/Cuemon.Core/Security/HashFactory.cs @@ -1,187 +1,12 @@ using System; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Provides access to factory methods for creating and configuring instances. /// public static class HashFactory { - /// - /// Creates an instance of a HMAC cryptographic implementation that derives from with the specified . Default is . - /// - /// The secret key for the encryption. - /// The that defines the HMAC cryptographic implementation. Default is . - /// The which may be configured. - /// A implementation of the by parameter specified . - /// - /// cannot be null. - /// - public static Hash CreateHmacCrypto(byte[] secret, KeyedCryptoAlgorithm algorithm = default, Action setup = null) - { - switch (algorithm) - { - case KeyedCryptoAlgorithm.HmacMd5: - return CreateHmacCryptoMd5(secret, setup); - case KeyedCryptoAlgorithm.HmacSha1: - return CreateHmacCryptoSha1(secret, setup); - case KeyedCryptoAlgorithm.HmacSha384: - return CreateHmacCryptoSha384(secret, setup); - case KeyedCryptoAlgorithm.HmacSha512: - return CreateHmacCryptoSha512(secret, setup); - default: - return CreateHmacCryptoSha256(secret, setup); - } - } - - /// - /// Creates an instance of . - /// - /// The secret key for encryption. The key can be any length. However, the recommended size is 128 bytes. If the key is more than 128 bytes long, it is hashed (using SHA-384) to derive a 128-byte key. If it is less than 128 bytes long, it is padded to 128 bytes. - /// The which may be configured. - /// A implementation of . - /// - /// cannot be null. - /// - public static Hash CreateHmacCryptoSha512(byte[] secret, Action setup = null) - { - Validator.ThrowIfNull(secret, nameof(secret)); - return new HmacSecureHashAlgorithm512(secret, setup); - } - - /// - /// Creates an instance of . - /// - /// The secret key for encryption. The key can be any length. However, the recommended size is 128 bytes. If the key is more than 128 bytes long, it is hashed (using SHA-384) to derive a 128-byte key. If it is less than 128 bytes long, it is padded to 128 bytes. - /// The which may be configured. - /// A implementation of . - /// - /// cannot be null. - /// - public static Hash CreateHmacCryptoSha384(byte[] secret, Action setup = null) - { - Validator.ThrowIfNull(secret, nameof(secret)); - return new HmacSecureHashAlgorithm384(secret, setup); - } - - /// - /// Creates an instance of . - /// - /// The secret key for encryption. The key can be any length. However, the recommended size is 64 bytes. If the key is more than 64 bytes long, it is hashed (using SHA-256) to derive a 64-byte key. If it is less than 64 bytes long, it is padded to 64 bytes. - /// The which may be configured. - /// A implementation of . - /// - /// cannot be null. - /// - public static Hash CreateHmacCryptoSha256(byte[] secret, Action setup = null) - { - Validator.ThrowIfNull(secret, nameof(secret)); - return new HmacSecureHashAlgorithm256(secret, setup); - } - - /// - /// Creates an instance of . - /// - /// The secret key for encryption. The key can be any length, but if it is more than 64 bytes long it will be hashed (using SHA-1) to derive a 64-byte key. Therefore, the recommended size of the secret key is 64 bytes. - /// The which may be configured. - /// A implementation of . - /// - /// cannot be null. - /// - public static Hash CreateHmacCryptoSha1(byte[] secret, Action setup = null) - { - Validator.ThrowIfNull(secret, nameof(secret)); - return new HmacSecureHashAlgorithm1(secret, setup); - } - - /// - /// Creates an instance of . - /// - /// The secret key for encryption. The key can be any length, but if it is more than 64 bytes long it will be hashed (using SHA-1) to derive a 64-byte key. Therefore, the recommended size of the secret key is 64 bytes. - /// The which may be configured. - /// A implementation of . - /// - /// cannot be null. - /// - public static Hash CreateHmacCryptoMd5(byte[] secret, Action setup = null) - { - Validator.ThrowIfNull(secret, nameof(secret)); - return new HmacMessageDigest5(secret, setup); - } - - /// - /// Creates an instance of a cryptographic implementation that derives from with the specified . Default is . - /// - /// The that defines the cryptographic implementation. Default is . - /// The which may be configured. - /// A implementation of the by parameter specified . - public static Hash CreateCrypto(CryptoAlgorithm algorithm = default, Action setup = null) - { - switch (algorithm) - { - case CryptoAlgorithm.Md5: - return CreateCryptoMd5(setup); - case CryptoAlgorithm.Sha1: - return CreateCryptoSha1(setup); - case CryptoAlgorithm.Sha384: - return CreateCryptoSha384(setup); - case CryptoAlgorithm.Sha512: - return CreateCryptoSha512(setup); - default: - return CreateCryptoSha256(setup); - } - } - - /// - /// Creates an instance of . - /// - /// The which may be configured. - /// A implementation of . - public static Hash CreateCryptoSha512(Action setup = null) - { - return new SecureHashAlgorithm512(setup); - } - - /// - /// Creates an instance of . - /// - /// The which may be configured. - /// A implementation of . - public static Hash CreateCryptoSha384(Action setup = null) - { - return new SecureHashAlgorithm384(setup); - } - - /// - /// Creates an instance of . - /// - /// The which may be configured. - /// A implementation of . - public static Hash CreateCryptoSha256(Action setup = null) - { - return new SecureHashAlgorithm256(setup); - } - - /// - /// Creates an instance of . - /// - /// The which may be configured. - /// A implementation of . - public static Hash CreateCryptoSha1(Action setup = null) - { - return new SecureHashAlgorithm1(setup); - } - - /// - /// Creates an instance of . - /// - /// The which may be configured. - /// A implementation of . - public static Hash CreateCryptoMd5(Action setup = null) - { - return new MessageDigest5(setup); - } - /// /// Creates an instance of a non-cryptographic implementation that derives from with the specified . Default is using . /// diff --git a/src/Cuemon.Core/Security/Cryptography/HashResult.cs b/src/Cuemon.Core/Security/HashResult.cs similarity index 98% rename from src/Cuemon.Core/Security/Cryptography/HashResult.cs rename to src/Cuemon.Core/Security/HashResult.cs index 91227277b..f31caf403 100644 --- a/src/Cuemon.Core/Security/Cryptography/HashResult.cs +++ b/src/Cuemon.Core/Security/HashResult.cs @@ -1,6 +1,6 @@ using System; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Represents the result of a computed checksum operation. @@ -9,7 +9,6 @@ public class HashResult : IEquatable { private readonly byte[] _input; - /// /// Initializes a new instance of the class. /// @@ -31,6 +30,7 @@ public HashResult(byte[] input) /// The copy of the original value that reflects a computed operation. public byte[] GetBytes() { + if (_input.Length == 0) { return new byte[0]; } var copy = new byte[_input.Length]; Array.Copy(_input, copy, copy.Length); return copy; diff --git a/src/Cuemon.Core/Security/Cryptography/IHash.cs b/src/Cuemon.Core/Security/IHash.cs similarity index 88% rename from src/Cuemon.Core/Security/Cryptography/IHash.cs rename to src/Cuemon.Core/Security/IHash.cs index 7eb25246c..fc2016a63 100644 --- a/src/Cuemon.Core/Security/Cryptography/IHash.cs +++ b/src/Cuemon.Core/Security/IHash.cs @@ -1,9 +1,9 @@ using System.IO; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// - /// Defines the bare minimum of cryptographic transformations. + /// Defines the bare minimum of both non-cryptographic and cryptographic transformations. /// public interface IHash { diff --git a/src/Cuemon.Core/Security/Cryptography/NonCryptoAlgorithm.cs b/src/Cuemon.Core/Security/NonCryptoAlgorithm.cs similarity index 95% rename from src/Cuemon.Core/Security/Cryptography/NonCryptoAlgorithm.cs rename to src/Cuemon.Core/Security/NonCryptoAlgorithm.cs index 165fc4134..617d7a250 100644 --- a/src/Cuemon.Core/Security/Cryptography/NonCryptoAlgorithm.cs +++ b/src/Cuemon.Core/Security/NonCryptoAlgorithm.cs @@ -1,4 +1,4 @@ -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { /// /// Specifies the different implementations of a non-cryptographic hashing algorithm. diff --git a/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj b/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj index feaa27a5a..18d1c1f65 100644 --- a/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj +++ b/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj @@ -18,6 +18,7 @@ + \ No newline at end of file diff --git a/src/Cuemon.Extensions.Net/Security/StringExtensions.cs b/src/Cuemon.Extensions.Net/Security/StringExtensions.cs index 677abd417..9860e6d53 100644 --- a/src/Cuemon.Extensions.Net/Security/StringExtensions.cs +++ b/src/Cuemon.Extensions.Net/Security/StringExtensions.cs @@ -39,7 +39,7 @@ public static Uri ToSignedUri(this string uriString, byte[] secret, DateTime? st if (start.HasValue) { qsc.Add(options.StartFieldName, Decorator.Enclose(start.Value).ToUtcKind().ToString("s") + "Z"); } if (expiry.HasValue) { qsc.Add(options.ExpiryFieldName, Decorator.Enclose(expiry.Value).ToUtcKind().ToString("s") + "Z"); } uriString = FormattableString.Invariant($"{uriString.SkipQueryString()}{qsc.ToQueryString(options.UrlEncode)}"); - qsc.Add(options.SignatureFieldName, HashFactory.CreateHmacCrypto(secret, options.Algorithm).ComputeHash(options.CanonicalRepresentationBuilder(uriString)).ToUrlEncodedBase64String()); + qsc.Add(options.SignatureFieldName, KeyedHashFactory.CreateHmacCrypto(secret, options.Algorithm).ComputeHash(options.CanonicalRepresentationBuilder(uriString)).ToUrlEncodedBase64String()); return new Uri(FormattableString.Invariant($"{uriString.SkipQueryString()}{qsc.ToQueryString(options.UrlEncode)}")); } @@ -71,7 +71,7 @@ public static void ValidateSignedUri(this string signedUriString, byte[] secret, if (string.IsNullOrWhiteSpace(signature)) { throw new SecurityException(message); } qsc.Remove(options.SignatureFieldName); - var computedSignature = HashFactory.CreateHmacCrypto(secret, options.Algorithm).ComputeHash(options.CanonicalRepresentationBuilder(FormattableString.Invariant($"{signedUriString.SkipQueryString()}{qsc.ToQueryString()}"))).ToUrlEncodedBase64String(); + var computedSignature = KeyedHashFactory.CreateHmacCrypto(secret, options.Algorithm).ComputeHash(options.CanonicalRepresentationBuilder(FormattableString.Invariant($"{signedUriString.SkipQueryString()}{qsc.ToQueryString()}"))).ToUrlEncodedBase64String(); if (!signature.Equals(computedSignature, StringComparison.Ordinal)) { throw new SecurityException(message); } } diff --git a/src/Cuemon.Core/Security/Cryptography/AesCryptor.cs b/src/Cuemon.Security.Cryptography/AesCryptor.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/AesCryptor.cs rename to src/Cuemon.Security.Cryptography/AesCryptor.cs diff --git a/src/Cuemon.Core/Security/Cryptography/AesCryptorOptions.cs b/src/Cuemon.Security.Cryptography/AesCryptorOptions.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/AesCryptorOptions.cs rename to src/Cuemon.Security.Cryptography/AesCryptorOptions.cs diff --git a/src/Cuemon.Core/Security/Cryptography/AesKeyOptions.cs b/src/Cuemon.Security.Cryptography/AesKeyOptions.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/AesKeyOptions.cs rename to src/Cuemon.Security.Cryptography/AesKeyOptions.cs diff --git a/src/Cuemon.Core/Security/Cryptography/AesSize.cs b/src/Cuemon.Security.Cryptography/AesSize.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/AesSize.cs rename to src/Cuemon.Security.Cryptography/AesSize.cs diff --git a/src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj b/src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj new file mode 100644 index 000000000..1e3160ae1 --- /dev/null +++ b/src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + 1b0bdf91-e7c7-4cb4-a39d-e1a5374c5602 + + + + Cuemon.Security.Cryptography + Cuemon.Security.Cryptography + The Cuemon.Security.Cryptography namespace contains . + + + + + + + + \ No newline at end of file diff --git a/src/Cuemon.Core/Security/Cryptography/HmacMessageDigest5.cs b/src/Cuemon.Security.Cryptography/HmacMessageDigest5.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/HmacMessageDigest5.cs rename to src/Cuemon.Security.Cryptography/HmacMessageDigest5.cs diff --git a/src/Cuemon.Core/Security/Cryptography/HmacSecureHashAlgorithm1.cs b/src/Cuemon.Security.Cryptography/HmacSecureHashAlgorithm1.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/HmacSecureHashAlgorithm1.cs rename to src/Cuemon.Security.Cryptography/HmacSecureHashAlgorithm1.cs diff --git a/src/Cuemon.Core/Security/Cryptography/HmacSecureHashAlgorithm256.cs b/src/Cuemon.Security.Cryptography/HmacSecureHashAlgorithm256.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/HmacSecureHashAlgorithm256.cs rename to src/Cuemon.Security.Cryptography/HmacSecureHashAlgorithm256.cs diff --git a/src/Cuemon.Core/Security/Cryptography/HmacSecureHashAlgorithm384.cs b/src/Cuemon.Security.Cryptography/HmacSecureHashAlgorithm384.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/HmacSecureHashAlgorithm384.cs rename to src/Cuemon.Security.Cryptography/HmacSecureHashAlgorithm384.cs diff --git a/src/Cuemon.Core/Security/Cryptography/HmacSecureHashAlgorithm512.cs b/src/Cuemon.Security.Cryptography/HmacSecureHashAlgorithm512.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/HmacSecureHashAlgorithm512.cs rename to src/Cuemon.Security.Cryptography/HmacSecureHashAlgorithm512.cs diff --git a/src/Cuemon.Core/Security/Cryptography/KeyedCryptoAlgorithm.cs b/src/Cuemon.Security.Cryptography/KeyedCryptoAlgorithm.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/KeyedCryptoAlgorithm.cs rename to src/Cuemon.Security.Cryptography/KeyedCryptoAlgorithm.cs diff --git a/src/Cuemon.Core/Security/Cryptography/KeyedCryptoHash.cs b/src/Cuemon.Security.Cryptography/KeyedCryptoHash.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/KeyedCryptoHash.cs rename to src/Cuemon.Security.Cryptography/KeyedCryptoHash.cs diff --git a/src/Cuemon.Security.Cryptography/KeyedHashFactory.cs b/src/Cuemon.Security.Cryptography/KeyedHashFactory.cs new file mode 100644 index 000000000..0f0ea8475 --- /dev/null +++ b/src/Cuemon.Security.Cryptography/KeyedHashFactory.cs @@ -0,0 +1,112 @@ +using System; + +namespace Cuemon.Security.Cryptography +{ + /// + /// Provides access to factory methods for creating and configuring instances based on . + /// + public static class KeyedHashFactory + { + /// + /// Creates an instance of a HMAC cryptographic implementation that derives from with the specified . Default is . + /// + /// The secret key for the encryption. + /// The that defines the HMAC cryptographic implementation. Default is . + /// The which may be configured. + /// A implementation of the by parameter specified . + /// + /// cannot be null. + /// + public static Hash CreateHmacCrypto(byte[] secret, KeyedCryptoAlgorithm algorithm = default, Action setup = null) + { + switch (algorithm) + { + case KeyedCryptoAlgorithm.HmacMd5: + return CreateHmacCryptoMd5(secret, setup); + case KeyedCryptoAlgorithm.HmacSha1: + return CreateHmacCryptoSha1(secret, setup); + case KeyedCryptoAlgorithm.HmacSha384: + return CreateHmacCryptoSha384(secret, setup); + case KeyedCryptoAlgorithm.HmacSha512: + return CreateHmacCryptoSha512(secret, setup); + default: + return CreateHmacCryptoSha256(secret, setup); + } + } + + /// + /// Creates an instance of . + /// + /// The secret key for encryption. The key can be any length. However, the recommended size is 128 bytes. If the key is more than 128 bytes long, it is hashed (using SHA-384) to derive a 128-byte key. If it is less than 128 bytes long, it is padded to 128 bytes. + /// The which may be configured. + /// A implementation of . + /// + /// cannot be null. + /// + public static Hash CreateHmacCryptoSha512(byte[] secret, Action setup = null) + { + Validator.ThrowIfNull(secret, nameof(secret)); + return new HmacSecureHashAlgorithm512(secret, setup); + } + + /// + /// Creates an instance of . + /// + /// The secret key for encryption. The key can be any length. However, the recommended size is 128 bytes. If the key is more than 128 bytes long, it is hashed (using SHA-384) to derive a 128-byte key. If it is less than 128 bytes long, it is padded to 128 bytes. + /// The which may be configured. + /// A implementation of . + /// + /// cannot be null. + /// + public static Hash CreateHmacCryptoSha384(byte[] secret, Action setup = null) + { + Validator.ThrowIfNull(secret, nameof(secret)); + return new HmacSecureHashAlgorithm384(secret, setup); + } + + /// + /// Creates an instance of . + /// + /// The secret key for encryption. The key can be any length. However, the recommended size is 64 bytes. If the key is more than 64 bytes long, it is hashed (using SHA-256) to derive a 64-byte key. If it is less than 64 bytes long, it is padded to 64 bytes. + /// The which may be configured. + /// A implementation of . + /// + /// cannot be null. + /// + public static Hash CreateHmacCryptoSha256(byte[] secret, Action setup = null) + { + Validator.ThrowIfNull(secret, nameof(secret)); + return new HmacSecureHashAlgorithm256(secret, setup); + } + + /// + /// Creates an instance of . + /// + /// The secret key for encryption. The key can be any length, but if it is more than 64 bytes long it will be hashed (using SHA-1) to derive a 64-byte key. Therefore, the recommended size of the secret key is 64 bytes. + /// The which may be configured. + /// A implementation of . + /// + /// cannot be null. + /// + public static Hash CreateHmacCryptoSha1(byte[] secret, Action setup = null) + { + Validator.ThrowIfNull(secret, nameof(secret)); + return new HmacSecureHashAlgorithm1(secret, setup); + } + + /// + /// Creates an instance of . + /// + /// The secret key for encryption. The key can be any length, but if it is more than 64 bytes long it will be hashed (using SHA-1) to derive a 64-byte key. Therefore, the recommended size of the secret key is 64 bytes. + /// The which may be configured. + /// A implementation of . + /// + /// cannot be null. + /// + public static Hash CreateHmacCryptoMd5(byte[] secret, Action setup = null) + { + Validator.ThrowIfNull(secret, nameof(secret)); + return new HmacMessageDigest5(secret, setup); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Core/Security/Cryptography/MessageDigest5.cs b/src/Cuemon.Security.Cryptography/MessageDigest5.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/MessageDigest5.cs rename to src/Cuemon.Security.Cryptography/MessageDigest5.cs diff --git a/src/Cuemon.Security.Cryptography/Properties/AssemblyInfo.cs b/src/Cuemon.Security.Cryptography/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..f297d4cf6 --- /dev/null +++ b/src/Cuemon.Security.Cryptography/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("7d28f2f4-baa5-4ec0-b14f-f7ed3417bfcd")] \ No newline at end of file diff --git a/src/Cuemon.Core/Security/Cryptography/SecureHashAlgorithm1.cs b/src/Cuemon.Security.Cryptography/SecureHashAlgorithm1.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/SecureHashAlgorithm1.cs rename to src/Cuemon.Security.Cryptography/SecureHashAlgorithm1.cs diff --git a/src/Cuemon.Core/Security/Cryptography/SecureHashAlgorithm256.cs b/src/Cuemon.Security.Cryptography/SecureHashAlgorithm256.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/SecureHashAlgorithm256.cs rename to src/Cuemon.Security.Cryptography/SecureHashAlgorithm256.cs diff --git a/src/Cuemon.Core/Security/Cryptography/SecureHashAlgorithm384.cs b/src/Cuemon.Security.Cryptography/SecureHashAlgorithm384.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/SecureHashAlgorithm384.cs rename to src/Cuemon.Security.Cryptography/SecureHashAlgorithm384.cs diff --git a/src/Cuemon.Core/Security/Cryptography/SecureHashAlgorithm512.cs b/src/Cuemon.Security.Cryptography/SecureHashAlgorithm512.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/SecureHashAlgorithm512.cs rename to src/Cuemon.Security.Cryptography/SecureHashAlgorithm512.cs diff --git a/src/Cuemon.Core/Security/Cryptography/CryptoAlgorithm.cs b/src/Cuemon.Security.Cryptography/UnkeyedCryptoAlgorithm.cs similarity index 95% rename from src/Cuemon.Core/Security/Cryptography/CryptoAlgorithm.cs rename to src/Cuemon.Security.Cryptography/UnkeyedCryptoAlgorithm.cs index 5086d0b51..782f2fbc7 100644 --- a/src/Cuemon.Core/Security/Cryptography/CryptoAlgorithm.cs +++ b/src/Cuemon.Security.Cryptography/UnkeyedCryptoAlgorithm.cs @@ -3,7 +3,7 @@ /// /// Specifies the different implementations of a cryptographic hashing algorithm. /// - public enum CryptoAlgorithm + public enum UnkeyedCryptoAlgorithm { /// /// The Message Digest 5 (MD5) algorithm (128 bits). diff --git a/src/Cuemon.Core/Security/Cryptography/UnkeyedCryptoHash.cs b/src/Cuemon.Security.Cryptography/UnkeyedCryptoHash.cs similarity index 100% rename from src/Cuemon.Core/Security/Cryptography/UnkeyedCryptoHash.cs rename to src/Cuemon.Security.Cryptography/UnkeyedCryptoHash.cs diff --git a/src/Cuemon.Security.Cryptography/UnkeyedHashFactory.cs b/src/Cuemon.Security.Cryptography/UnkeyedHashFactory.cs new file mode 100644 index 000000000..48a88ece2 --- /dev/null +++ b/src/Cuemon.Security.Cryptography/UnkeyedHashFactory.cs @@ -0,0 +1,83 @@ +using System; + +namespace Cuemon.Security.Cryptography +{ + /// + /// Provides access to factory methods for creating and configuring instances based on . + /// + public static class UnkeyedHashFactory + { + /// + /// Creates an instance of a cryptographic implementation that derives from with the specified . Default is . + /// + /// The that defines the cryptographic implementation. Default is . + /// The which may be configured. + /// A implementation of the by parameter specified . + public static Hash CreateCrypto(UnkeyedCryptoAlgorithm algorithm = default, Action setup = null) + { + switch (algorithm) + { + case UnkeyedCryptoAlgorithm.Md5: + return CreateCryptoMd5(setup); + case UnkeyedCryptoAlgorithm.Sha1: + return CreateCryptoSha1(setup); + case UnkeyedCryptoAlgorithm.Sha384: + return CreateCryptoSha384(setup); + case UnkeyedCryptoAlgorithm.Sha512: + return CreateCryptoSha512(setup); + default: + return CreateCryptoSha256(setup); + } + } + + /// + /// Creates an instance of . + /// + /// The which may be configured. + /// A implementation of . + public static Hash CreateCryptoSha512(Action setup = null) + { + return new SecureHashAlgorithm512(setup); + } + + /// + /// Creates an instance of . + /// + /// The which may be configured. + /// A implementation of . + public static Hash CreateCryptoSha384(Action setup = null) + { + return new SecureHashAlgorithm384(setup); + } + + /// + /// Creates an instance of . + /// + /// The which may be configured. + /// A implementation of . + public static Hash CreateCryptoSha256(Action setup = null) + { + return new SecureHashAlgorithm256(setup); + } + + /// + /// Creates an instance of . + /// + /// The which may be configured. + /// A implementation of . + public static Hash CreateCryptoSha1(Action setup = null) + { + return new SecureHashAlgorithm1(setup); + } + + /// + /// Creates an instance of . + /// + /// The which may be configured. + /// A implementation of . + public static Hash CreateCryptoMd5(Action setup = null) + { + return new MessageDigest5(setup); + } + } +} \ No newline at end of file From d3d04847cd7116c4879a653ac2ded072b75ce5da Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 17:02:57 +0200 Subject: [PATCH 102/385] Refactoring of Integrity related classes; opt-in for more open architecture. Incl. consequence changes. --- src/Cuemon.Data.Integrity/CacheValidator.cs | 264 +++++------------- .../CacheValidatorFactory.cs | 31 +- .../CacheValidatorOptions.cs | 42 --- src/Cuemon.Data.Integrity/ChecksumBuilder.cs | 115 ++------ .../ChecksumBuilderOptions.cs | 37 --- ...th.cs => EntityDataIntegrityValidation.cs} | 2 +- src/Cuemon.Data.Integrity/EntityInfo.cs | 53 ++++ .../ChecksumBuilderDecoratorExtensions.cs | 67 ++--- .../FileChecksumOptions.cs | 11 - src/Cuemon.Data.Integrity/IDataIntegrity.cs | 2 +- .../IEntityDataIntegrity.cs | 3 +- .../{IEntityData.cs => IEntityInfo.cs} | 2 +- .../CacheableObjectResultExtensions.cs | 4 +- .../Configuration/AssemblyCacheBusting.cs | 6 +- .../Integrity/CacheValidatorExtensions.cs | 2 +- .../AssemblyExtensions.cs | 19 +- .../ChecksumBuilderExtensions.cs | 110 ++++---- .../DateTimeExtensions.cs | 153 +--------- .../FileInfoExtensions.cs | 6 +- .../GlobalSuppressions.cs | 8 - .../CacheableObjectTest.cs | 8 +- .../AssemblyExtensionsTest.cs | 13 +- .../DateTimeExtensionsTest.cs | 32 ++- 23 files changed, 310 insertions(+), 680 deletions(-) delete mode 100644 src/Cuemon.Data.Integrity/CacheValidatorOptions.cs delete mode 100644 src/Cuemon.Data.Integrity/ChecksumBuilderOptions.cs rename src/Cuemon.Data.Integrity/{EntityDataIntegrityStrength.cs => EntityDataIntegrityValidation.cs} (92%) create mode 100644 src/Cuemon.Data.Integrity/EntityInfo.cs rename src/Cuemon.Data.Integrity/{IEntityData.cs => IEntityInfo.cs} (83%) delete mode 100644 src/Cuemon.Extensions.Data.Integrity/GlobalSuppressions.cs diff --git a/src/Cuemon.Data.Integrity/CacheValidator.cs b/src/Cuemon.Data.Integrity/CacheValidator.cs index 454008687..c3beb53bf 100644 --- a/src/Cuemon.Data.Integrity/CacheValidator.cs +++ b/src/Cuemon.Data.Integrity/CacheValidator.cs @@ -4,20 +4,37 @@ using System.Linq; using System.Reflection; using Cuemon.Collections.Generic; +using Cuemon.Security; namespace Cuemon.Data.Integrity { /// /// Provides a way to represent cacheable data-centric content that can be validated by cache-aware applications. /// - public class CacheValidator : ChecksumBuilder, IEntityDataTimestamp + public class CacheValidator : ChecksumBuilder, IEntityInfo { private const long NullOrZeroLengthChecksum = 23719; - private static readonly CacheValidator DefaultCacheValidatorValue = new CacheValidator(DateTime.MinValue, DateTime.MinValue); + private static readonly CacheValidator DefaultCacheValidatorValue = new CacheValidator(new EntityInfo(DateTime.MinValue, DateTime.MinValue), () => Security.HashFactory.CreateFnv128()); private static CacheValidator _referencePointCacheValidator; private static Assembly _assemblyValue; private static readonly Lazy LazyAssembly = new Lazy(() => Assembly.GetEntryAssembly() ?? typeof(ChecksumBuilder).GetTypeInfo().Assembly); + /// + /// Gets the most significant object from the most significant (largest) value of either or in the specified . + /// + /// A sequence of objects to parse for the most significant (largest) value of either or . + /// The most significant object from the specified . + public static CacheValidator GetMostSignificant(params CacheValidator[] sequence) + { + Validator.ThrowIfNull(sequence, nameof(sequence)); + var mostSignificant = Default; + foreach (var candidate in sequence) + { + if (candidate.GetMostSignificant().Ticks > mostSignificant.GetMostSignificant().Ticks) { mostSignificant = candidate; } + } + return mostSignificant; + } + /// /// Gets or sets the that will serve as the ideal candidate for a reference point. Default is with a fallback to Cuemon.Core.dll. /// @@ -36,180 +53,64 @@ public static Assembly AssemblyReference } } - private CacheValidator() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value for when data this instance represents was first created. - /// The which need to be configured. - public CacheValidator(DateTime created, Action setup = null) - : this(created, created, setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value for when data this instance represents was first created. - /// A value for when data this instance represents was last modified. - /// The which need to be configured. - public CacheValidator(DateTime created, DateTime? modified, Action setup = null) - : this(created, modified, (byte[])null, setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value for when data this instance represents was first created. - /// A value for when data this instance represents was last modified. - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public CacheValidator(DateTime created, DateTime? modified, double checksum, Action setup = null) - : this(created, modified, Convertible.GetBytes(checksum), setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value for when data this instance represents was first created. - /// A value for when data this instance represents was last modified. - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public CacheValidator(DateTime created, DateTime? modified, short checksum, Action setup = null) - : this(created, modified, Convertible.GetBytes(checksum), setup) - { - } - - - /// - /// Initializes a new instance of the class. - /// - /// A value for when data this instance represents was first created. - /// A value for when data this instance represents was last modified. - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public CacheValidator(DateTime created, DateTime? modified, string checksum, Action setup = null) - : this(created, modified, Generate.HashCode64(checksum), setup) - { - } - /// - /// Initializes a new instance of the class. - /// - /// A value for when data this instance represents was first created. - /// A value for when data this instance represents was last modified. - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public CacheValidator(DateTime created, DateTime? modified, int checksum, Action setup = null) - : this(created, modified, Convertible.GetBytes(checksum), setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value for when data this instance represents was first created. - /// A value for when data this instance represents was last modified. - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public CacheValidator(DateTime created, DateTime? modified, long checksum, Action setup = null) - : this(created, modified, Convertible.GetBytes(checksum), setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value for when data this instance represents was first created. - /// A value for when data this instance represents was last modified. - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public CacheValidator(DateTime created, DateTime? modified, float checksum, Action setup = null) - : this(created, modified, Convertible.GetBytes(checksum), setup) - { - } - - /// - /// Initializes a new instance of the class. + /// Gets a object that is initialized to a default representation that should be considered invalid for usage beyond this check. /// - /// A value for when data this instance represents was first created. - /// A value for when data this instance represents was last modified. - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public CacheValidator(DateTime created, DateTime? modified, ushort checksum, Action setup = null) - : this(created, modified, Convertible.GetBytes(checksum), setup) - { - } + /// A object that is initialized to a default representation. + public static CacheValidator Default => DefaultCacheValidatorValue.Clone(); /// - /// Initializes a new instance of the class. + /// Gets a object that represents an reference point. /// - /// A value for when data this instance represents was first created. - /// A value for when data this instance represents was last modified. - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public CacheValidator(DateTime created, DateTime? modified, uint checksum, Action setup = null) - : this(created, modified, Convertible.GetBytes(checksum), setup) + /// A object that represents an reference point. + public static CacheValidator ReferencePoint { + get + { + if (_referencePointCacheValidator == null) + { + _referencePointCacheValidator = CacheValidatorFactory.CreateValidator(AssemblyReference); + } + return _referencePointCacheValidator.Clone(); + } } - /// - /// Initializes a new instance of the class. - /// - /// A value for when data this instance represents was first created. - /// A value for when data this instance represents was last modified. - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public CacheValidator(DateTime created, DateTime? modified, ulong checksum, Action setup = null) - : this(created, modified, Convertible.GetBytes(checksum), setup) + private CacheValidator(Func hashFactory) : base(hashFactory) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// A value for when data this instance represents was first created. - /// A value for when data this instance represents was last modified. - /// An array of bytes containing a checksum of the data this instance represents. - /// The which may be configured. - public CacheValidator(DateTime created, DateTime? modified, byte[] checksum, Action setup = null) + /// An object that representing the meta-data of an entity. + /// The function delegate that is invoked to produce the . + /// A enumeration value that indicates how a checksum is manipulated. Default is . + /// method + public CacheValidator(EntityInfo entity, Func hashFactory, EntityDataIntegrityMethod method = EntityDataIntegrityMethod.Unaltered) : base(entity?.Checksum.GetBytes(), hashFactory) { - var options = Patterns.Configure(setup); - var isChecksumNullOrZeroLength = (checksum == null || checksum.Length == 0); + Validator.ThrowIfNull(entity, nameof(entity)); + + Created = entity.Created; + Modified = entity.Modified; + Validation = entity.Validation; + Method = method; - Created = created.ToUniversalTime(); - Modified = modified?.ToUniversalTime(); - - var strength = isChecksumNullOrZeroLength ? EntityDataIntegrityStrength.Unspecified : EntityDataIntegrityStrength.Strong; - switch (options.Method) + switch (method) { case EntityDataIntegrityMethod.Unaltered: break; case EntityDataIntegrityMethod.Timestamp: - checksum = Convertible.GetBytes(Created.Ticks ^ Modified?.Ticks ?? DateTime.MinValue.Ticks); - strength = EntityDataIntegrityStrength.Weak; + Bytes = new List(Convertible.GetBytes(Created.Ticks ^ Modified?.Ticks ?? DateTime.MinValue.Ticks)); break; case EntityDataIntegrityMethod.Combined: - var checksumValue = isChecksumNullOrZeroLength ? NullOrZeroLengthChecksum : Generate.HashCode64(checksum.Cast()); - checksum = Convertible.GetBytes(Created.Ticks ^ Modified?.Ticks ?? DateTime.MinValue.Ticks ^ checksumValue); - strength = isChecksumNullOrZeroLength ? EntityDataIntegrityStrength.Weak : EntityDataIntegrityStrength.Strong; + var checksumValue = entity.Checksum.HasValue ? Generate.HashCode64(Bytes.Cast()) : NullOrZeroLengthChecksum; + Bytes = new List(Convertible.GetBytes(Created.Ticks ^ Modified?.Ticks ?? DateTime.MinValue.Ticks ^ checksumValue)); break; default: - throw new InvalidEnumArgumentException(nameof(setup), (int)options.Method, typeof(EntityDataIntegrityMethod)); + throw new InvalidEnumArgumentException(nameof(method), (int)method, typeof(EntityDataIntegrityMethod)); } - Bytes = checksum == null ? new List() : new List(checksum); - Strength = strength; - Method = options.Method; - Options = options; - Algorithm = options.Algorithm; + } - - private CacheValidatorOptions Options { get; set; } /// /// Gets a value from when data this instance represents was first created, expressed as the Coordinated Universal Time (UTC). @@ -223,28 +124,6 @@ public CacheValidator(DateTime created, DateTime? modified, byte[] checksum, Act /// A value from when data this instance represents was last modified, expressed as the Coordinated Universal Time (UTC). public DateTime? Modified { get; private set; } - /// - /// Gets a object that is initialized to a default representation that should be considered invalid for usage beyond this check. - /// - /// A object that is initialized to a default representation. - public static CacheValidator Default => DefaultCacheValidatorValue.Clone(); - - /// - /// Gets a object that represents an reference point. - /// - /// A object that represents an reference point. - public static CacheValidator ReferencePoint - { - get - { - if (_referencePointCacheValidator == null) - { - _referencePointCacheValidator = CacheValidatorFactory.CreateValidator(AssemblyReference); - } - return _referencePointCacheValidator.Clone(); - } - } - /// /// Gets an enumeration value of indicating the usage method of this instance. /// @@ -252,10 +131,22 @@ public static CacheValidator ReferencePoint public EntityDataIntegrityMethod Method { get; private set; } /// - /// Gets an enumeration value of indicating the strength of this instance. + /// Gets an enumeration value of indicating the strength of this instance. /// - /// One of the enumeration values of that specifies the strength of this instance. - public EntityDataIntegrityStrength Strength { get; private set; } + /// One of the enumeration values of that specifies the strength of this instance. + public EntityDataIntegrityValidation Validation { get; private set; } + + /// + /// Combines the to the representation of this instance. + /// + /// A containing a checksum of the additional data this instance must represent. + /// A reference to this instance after the operation has completed. + public override ChecksumBuilder CombineWith(byte[] additionalChecksum) + { + var isChecksumNullOrZeroLength = (additionalChecksum == null || additionalChecksum.Length == 0); + if (isChecksumNullOrZeroLength) { Validation = EntityDataIntegrityValidation.Strong; } + return base.CombineWith(additionalChecksum); + } /// /// Creates a shallow copy of the current object. @@ -263,13 +154,12 @@ public static CacheValidator ReferencePoint /// A new that is a copy of this instance. public virtual CacheValidator Clone() { - return new CacheValidator() + return new CacheValidator(HashFactory) { - Options = Options, Method = Method, Modified = Modified, Created = Created, - Strength = Strength, + Validation = Validation, Bytes = Bytes.ToList(), ComputedHash = ComputedHash }; @@ -283,21 +173,5 @@ public DateTime GetMostSignificant() { return Arguments.ToEnumerableOf(Created, Modified ?? DateTime.MinValue).Max(); } - - /// - /// Gets the most significant object from the most significant (largest) value of either or in the specified . - /// - /// A sequence of objects to parse for the most significant (largest) value of either or . - /// The most significant object from the specified . - public static CacheValidator GetMostSignificant(params CacheValidator[] sequence) - { - Validator.ThrowIfNull(sequence, nameof(sequence)); - var mostSignificant = Default; - foreach (var candidate in sequence) - { - if (candidate.GetMostSignificant().Ticks > mostSignificant.GetMostSignificant().Ticks) { mostSignificant = candidate; } - } - return mostSignificant; - } } } \ No newline at end of file diff --git a/src/Cuemon.Data.Integrity/CacheValidatorFactory.cs b/src/Cuemon.Data.Integrity/CacheValidatorFactory.cs index 36fcf2969..ead129999 100644 --- a/src/Cuemon.Data.Integrity/CacheValidatorFactory.cs +++ b/src/Cuemon.Data.Integrity/CacheValidatorFactory.cs @@ -1,7 +1,7 @@ using System; using System.IO; -using System.Linq; using System.Reflection; +using Cuemon.Security; namespace Cuemon.Data.Integrity { @@ -14,14 +14,16 @@ public static class CacheValidatorFactory /// Creates and returns an instance of from the specified . /// /// The to convert. + /// The function delegate that is invoked to produce the . Default is . /// The which may be configured. /// A that represents the . /// /// cannot be null. /// - public static CacheValidator CreateValidator(FileInfo file, Action setup = null) + public static CacheValidator CreateValidator(FileInfo file, Func hashFactory = null, Action setup = null) { Validator.ThrowIfNull(file, nameof(file)); + if (hashFactory == null) { hashFactory = () => HashFactory.CreateFnv128(); } var options = Patterns.Configure(setup); return DataIntegrityFactory.CreateIntegrity(file, fio => { @@ -30,18 +32,10 @@ public static CacheValidator CreateValidator(FileInfo file, Action 0) { - return new CacheValidator(fi.CreationTimeUtc, fi.LastWriteTimeUtc, Generate.HashCode64(checksumBytes.Cast()), o => - { - o.Method = options.Method; - o.Algorithm = options.Algorithm; - }); + return new CacheValidator(new EntityInfo(fi.CreationTimeUtc, fi.LastWriteTimeUtc, checksumBytes, EntityDataIntegrityValidation.Strong), hashFactory); } var fileNameHashCode64 = Generate.HashCode64(file.FullName); - return new CacheValidator(fi.CreationTimeUtc, fi.LastWriteTimeUtc, fileNameHashCode64, o => - { - o.Method = options.Method; - o.Algorithm = options.Algorithm; - }); + return new CacheValidator(new EntityInfo(fi.CreationTimeUtc, fi.LastWriteTimeUtc, Convertible.GetBytes(fileNameHashCode64)), hashFactory, options.Method); }; }) as CacheValidator; } @@ -50,15 +44,22 @@ public static CacheValidator CreateValidator(FileInfo file, Action from the specified . /// /// The to convert. + /// The function delegate that is invoked to produce the . Default is . /// The which may be configured. /// A that represents the . - public static CacheValidator CreateValidator(Assembly assembly, Action setup = null) + /// + /// cannot be null. + /// + public static CacheValidator CreateValidator(Assembly assembly, Func hashFactory = null, Action setup = null) { + Validator.ThrowIfNull(assembly, nameof(assembly)); + if (hashFactory == null) { hashFactory = () => HashFactory.CreateFnv128(); } + var options = Patterns.Configure(setup); var assemblyHashCode64 = Generate.HashCode64(assembly.FullName); var assemblyLocation = assembly.Location; return assembly.IsDynamic - ? new CacheValidator(DateTime.MinValue, DateTime.MaxValue, assemblyHashCode64, Patterns.ConfigureExchange(setup)) - : CreateValidator(new FileInfo(assemblyLocation), setup); + ? new CacheValidator(new EntityInfo(DateTime.MinValue, DateTime.MaxValue, Convertible.GetBytes(assemblyHashCode64)), hashFactory, options.Method) + : CreateValidator(new FileInfo(assemblyLocation), hashFactory, setup); } } } \ No newline at end of file diff --git a/src/Cuemon.Data.Integrity/CacheValidatorOptions.cs b/src/Cuemon.Data.Integrity/CacheValidatorOptions.cs deleted file mode 100644 index c23a461fe..000000000 --- a/src/Cuemon.Data.Integrity/CacheValidatorOptions.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Cuemon.Security.Cryptography; - -namespace Cuemon.Data.Integrity -{ - /// - /// Configuration options for . - /// - public class CacheValidatorOptions : ChecksumBuilderOptions - { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public CacheValidatorOptions() - { - Algorithm = CryptoAlgorithm.Md5; - Method = EntityDataIntegrityMethod.Unaltered; - } - - /// - /// Gets an enumeration value of indicating how a checksum is generated. - /// - /// One of the enumeration values of that indicates how a checksum is generated. - public EntityDataIntegrityMethod Method { get; set; } - } -} \ No newline at end of file diff --git a/src/Cuemon.Data.Integrity/ChecksumBuilder.cs b/src/Cuemon.Data.Integrity/ChecksumBuilder.cs index d5fb3adf4..0a0d62e32 100644 --- a/src/Cuemon.Data.Integrity/ChecksumBuilder.cs +++ b/src/Cuemon.Data.Integrity/ChecksumBuilder.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Cuemon.Security.Cryptography; +using Cuemon.Security; namespace Cuemon.Data.Integrity { @@ -12,109 +12,28 @@ public class ChecksumBuilder : IDataIntegrity, IEquatable /// /// Initializes a new instance of the class. /// - public ChecksumBuilder() : this((byte[])null) + /// The function delegate that is invoked to produce the . + public ChecksumBuilder(Func hashFactory) : this(null, hashFactory) { } /// /// Initializes a new instance of the class. /// - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public ChecksumBuilder(double checksum, Action setup = null) : this(Convertible.GetBytes(checksum), setup) + /// A containing a checksum of the data this instance represents. + /// The function delegate that is invoked to produce the . + public ChecksumBuilder(byte[] checksum, Func hashFactory) { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public ChecksumBuilder(short checksum, Action setup = null) : this(Convertible.GetBytes(checksum), setup) - { - } - - - /// - /// Initializes a new instance of the class. - /// - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public ChecksumBuilder(string checksum, Action setup = null) : this(Generate.HashCode64(checksum), setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public ChecksumBuilder(int checksum, Action setup = null) : this(Convertible.GetBytes(checksum), setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public ChecksumBuilder(long checksum, Action setup = null) : this(Convertible.GetBytes(checksum), setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public ChecksumBuilder(float checksum, Action setup = null) : this(Convertible.GetBytes(checksum), setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public ChecksumBuilder(ushort checksum, Action setup = null) : this(Convertible.GetBytes(checksum), setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public ChecksumBuilder(uint checksum, Action setup = null) : this(Convertible.GetBytes(checksum), setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value containing a byte-for-byte checksum of the data this instance represents. - /// The which need to be configured. - public ChecksumBuilder(ulong checksum, Action setup = null) : this(Convertible.GetBytes(checksum), setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// An array of bytes containing a checksum of the data this instance represents. - /// The which may be configured. - public ChecksumBuilder(byte[] checksum, Action setup = null) - { - var options = Patterns.Configure(setup); - Algorithm = options.Algorithm; + Validator.ThrowIfNull(hashFactory, nameof(hashFactory)); Bytes = checksum == null ? new List() : new List(checksum); + HashFactory = hashFactory; } /// - /// Gets the hash algorithm to use for the checksum computation. + /// Gets the value factory of this instance. /// - /// The hash algorithm to use for the checksum computation. - public CryptoAlgorithm Algorithm { get; protected set; } + /// The value factory of this instance. + protected Func HashFactory { get; } /// /// Gets a byte array that is the result of the associated . @@ -126,7 +45,7 @@ public ChecksumBuilder(byte[] checksum, Action setup = n /// Gets a containing a computed hash value of the data this instance represents. /// /// A containing a computed hash value of the data this instance represents. - public HashResult Checksum => ComputedHash ?? (ComputedHash = HashFactory.CreateCrypto(Algorithm).ComputeHash(Bytes.ToArray())); + public HashResult Checksum => ComputedHash ?? (ComputedHash = HashFactory.Invoke().ComputeHash(Bytes.ToArray())); /// /// Gets or sets the computed checksum of . @@ -135,13 +54,15 @@ public ChecksumBuilder(byte[] checksum, Action setup = n protected HashResult ComputedHash { get; set; } /// - /// Provides a way to append additional checksum to the representation of this instance. + /// Combines the to the representation of this instance. /// - /// A containing a checksum of the additional data this instance must represent. - public void AppendChecksum(byte[] checksum) + /// A containing a checksum of the additional data this instance must represent. + /// A reference to this instance after the operation has completed. + public virtual ChecksumBuilder CombineWith(byte[] additionalChecksum) { ComputedHash = null; - Bytes.AddRange(checksum); + Bytes.AddRange(additionalChecksum); + return this; } /// diff --git a/src/Cuemon.Data.Integrity/ChecksumBuilderOptions.cs b/src/Cuemon.Data.Integrity/ChecksumBuilderOptions.cs deleted file mode 100644 index 72ec4cf9a..000000000 --- a/src/Cuemon.Data.Integrity/ChecksumBuilderOptions.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Cuemon.Security.Cryptography; - -namespace Cuemon.Data.Integrity -{ - /// - /// Configuration options for . - /// - public class ChecksumBuilderOptions - { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// - /// - /// - /// - public ChecksumBuilderOptions() - { - Algorithm = CryptoAlgorithm.Md5; - } - - /// - /// Gets or sets the hash algorithm to use for the checksum computation. - /// - /// The hash algorithm to use for the checksum computation. - public CryptoAlgorithm Algorithm { get; set; } - } -} \ No newline at end of file diff --git a/src/Cuemon.Data.Integrity/EntityDataIntegrityStrength.cs b/src/Cuemon.Data.Integrity/EntityDataIntegrityValidation.cs similarity index 92% rename from src/Cuemon.Data.Integrity/EntityDataIntegrityStrength.cs rename to src/Cuemon.Data.Integrity/EntityDataIntegrityValidation.cs index 605cc63b7..ae0afcfab 100644 --- a/src/Cuemon.Data.Integrity/EntityDataIntegrityStrength.cs +++ b/src/Cuemon.Data.Integrity/EntityDataIntegrityValidation.cs @@ -3,7 +3,7 @@ /// /// Specifies the validation strength of a data checksum. /// - public enum EntityDataIntegrityStrength + public enum EntityDataIntegrityValidation { /// /// Indicates that no checksum strength was specified. diff --git a/src/Cuemon.Data.Integrity/EntityInfo.cs b/src/Cuemon.Data.Integrity/EntityInfo.cs new file mode 100644 index 000000000..1553703b3 --- /dev/null +++ b/src/Cuemon.Data.Integrity/EntityInfo.cs @@ -0,0 +1,53 @@ +using System; +using Cuemon.Security; + +namespace Cuemon.Data.Integrity +{ + /// + /// Represents the metadata information normally associated with an entity/resource. + /// Implements the + /// + /// + public class EntityInfo : IEntityInfo + { + /// + /// Initializes a new instance of the class. + /// + /// A value for when data this instance represents was first created. + public EntityInfo(DateTime created) : this(created, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A value for when data this instance represents was first created. + /// A value for when data this instance represents was last modified. + public EntityInfo(DateTime created, DateTime? modified) : this(created, modified, null, EntityDataIntegrityValidation.Unspecified) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A value for when data this instance represents was first created. + /// A value for when data this instance represents was last modified. + /// A containing a checksum of the data this instance represents. + /// A enumeration value that indicates the validation strength of the specified . Default is . + public EntityInfo(DateTime created, DateTime? modified, byte[] checksum, EntityDataIntegrityValidation validation = EntityDataIntegrityValidation.Weak) + { + Created = created.ToUniversalTime(); + Modified = modified?.ToUniversalTime(); + Checksum = new HashResult(checksum); + Validation = validation; + } + + public DateTime Created { get; } + + public DateTime? Modified { get; } + + public HashResult Checksum { get; } + + public EntityDataIntegrityValidation Validation { get; } + } +} \ No newline at end of file diff --git a/src/Cuemon.Data.Integrity/Extensions/ChecksumBuilderDecoratorExtensions.cs b/src/Cuemon.Data.Integrity/Extensions/ChecksumBuilderDecoratorExtensions.cs index 10945d500..bdb877c73 100644 --- a/src/Cuemon.Data.Integrity/Extensions/ChecksumBuilderDecoratorExtensions.cs +++ b/src/Cuemon.Data.Integrity/Extensions/ChecksumBuilderDecoratorExtensions.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; namespace Cuemon.Data.Integrity { @@ -16,14 +14,14 @@ public static class ChecksumBuilderDecoratorExtensions /// /// The type of the . /// The to extend. - /// A array that contains zero or more checksum of the additional data the enclosed of the must represent. + /// A value containing a checksum of the additional data the enclosed of the must represent. /// An updated instance of the enclosed of the . /// /// cannot be null. /// - public static T CombineWith(this IDecorator decorator, params double[] additionalChecksum) where T : ChecksumBuilder + public static T CombineWith(this IDecorator decorator, double additionalChecksum) where T : ChecksumBuilder { - return CombineWith(decorator, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return CombineWith(decorator, Convertible.GetBytes(additionalChecksum)); } /// @@ -31,14 +29,14 @@ public static T CombineWith(this IDecorator decorator, params double[] add /// /// The type of the . /// The to extend. - /// An array that contains zero or more checksum of the additional data the enclosed of the must represent. + /// A value containing a checksum of the additional data the enclosed of the must represent. /// An updated instance of the enclosed of the . /// /// cannot be null. /// - public static T CombineWith(this IDecorator decorator, params short[] additionalChecksum) where T : ChecksumBuilder + public static T CombineWith(this IDecorator decorator, short additionalChecksum) where T : ChecksumBuilder { - return CombineWith(decorator, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return CombineWith(decorator, Convertible.GetBytes(additionalChecksum)); } /// @@ -46,19 +44,14 @@ public static T CombineWith(this IDecorator decorator, params short[] addi /// /// The type of the . /// The to extend. - /// A array that contains zero or more checksum of the additional data the enclosed of the must represent. + /// A containing a checksum of the additional data the enclosed of the must represent. /// An updated instance of the enclosed of the . /// /// cannot be null. /// - public static T CombineWith(this IDecorator decorator, params string[] additionalChecksum) where T : ChecksumBuilder + public static T CombineWith(this IDecorator decorator, string additionalChecksum) where T : ChecksumBuilder { - var result = new List(); - for (int i = 0; i < additionalChecksum.Length; i++) - { - result.Add(Generate.HashCode64(additionalChecksum[i])); - } - return CombineWith(decorator, result.ToArray()); + return CombineWith(decorator, Generate.HashCode64(additionalChecksum)); } /// @@ -66,14 +59,14 @@ public static T CombineWith(this IDecorator decorator, params string[] add /// /// The type of the . /// The to extend. - /// An array that contains zero or more checksum of the additional data the enclosed of the must represent. + /// A value containing a checksum of the additional data the enclosed of the must represent. /// An updated instance of the enclosed of the . /// /// cannot be null. /// - public static T CombineWith(this IDecorator decorator, params int[] additionalChecksum) where T : ChecksumBuilder + public static T CombineWith(this IDecorator decorator, int additionalChecksum) where T : ChecksumBuilder { - return CombineWith(decorator, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return CombineWith(decorator, Convertible.GetBytes(additionalChecksum)); } /// @@ -81,14 +74,14 @@ public static T CombineWith(this IDecorator decorator, params int[] additi /// /// The type of the . /// The to extend. - /// An array that contains zero or more checksum of the additional data the enclosed of the must represent. + /// A value containing a checksum of the additional data the enclosed of the must represent. /// An updated instance of the enclosed of the . /// /// cannot be null. /// - public static T CombineWith(this IDecorator decorator, params long[] additionalChecksum) where T : ChecksumBuilder + public static T CombineWith(this IDecorator decorator, long additionalChecksum) where T : ChecksumBuilder { - return CombineWith(decorator, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return CombineWith(decorator, Convertible.GetBytes(additionalChecksum)); } /// @@ -96,14 +89,14 @@ public static T CombineWith(this IDecorator decorator, params long[] addit /// /// The type of the . /// The to extend. - /// A array that contains zero or more checksum of the additional data the enclosed of the must represent. + /// A value containing a checksum of the additional data the enclosed of the must represent. /// An updated instance of the enclosed of the . /// /// cannot be null. /// - public static T CombineWith(this IDecorator decorator, params float[] additionalChecksum) where T : ChecksumBuilder + public static T CombineWith(this IDecorator decorator, float additionalChecksum) where T : ChecksumBuilder { - return CombineWith(decorator, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return CombineWith(decorator, Convertible.GetBytes(additionalChecksum)); } /// @@ -111,14 +104,14 @@ public static T CombineWith(this IDecorator decorator, params float[] addi /// /// The type of the . /// The to extend. - /// An array that contains zero or more checksum of the additional data the enclosed of the must represent. + /// A value containing a checksum of the additional data the enclosed of the must represent. /// An updated instance of the enclosed of the . /// /// cannot be null. /// - public static T CombineWith(this IDecorator decorator, params ushort[] additionalChecksum) where T : ChecksumBuilder + public static T CombineWith(this IDecorator decorator, ushort additionalChecksum) where T : ChecksumBuilder { - return CombineWith(decorator, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return CombineWith(decorator, Convertible.GetBytes(additionalChecksum)); } /// @@ -126,14 +119,14 @@ public static T CombineWith(this IDecorator decorator, params ushort[] add /// /// The type of the . /// The to extend. - /// An array that contains zero or more checksum of the additional data the enclosed of the must represent. + /// A value containing a checksum of the additional data the enclosed of the must represent. /// An updated instance of the enclosed of the . /// /// cannot be null. /// - public static T CombineWith(this IDecorator decorator, params uint[] additionalChecksum) where T : ChecksumBuilder + public static T CombineWith(this IDecorator decorator, uint additionalChecksum) where T : ChecksumBuilder { - return CombineWith(decorator, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return CombineWith(decorator, Convertible.GetBytes(additionalChecksum)); } /// @@ -141,14 +134,14 @@ public static T CombineWith(this IDecorator decorator, params uint[] addit /// /// The type of the . /// The to extend. - /// An array that contains zero or more checksum of the additional data the enclosed of the must represent. + /// A value containing a checksum of the additional data the enclosed of the must represent. /// An updated instance of the enclosed of the . /// /// cannot be null. /// - public static T CombineWith(this IDecorator decorator, params ulong[] additionalChecksum) where T : ChecksumBuilder + public static T CombineWith(this IDecorator decorator, ulong additionalChecksum) where T : ChecksumBuilder { - return CombineWith(decorator, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return CombineWith(decorator, Convertible.GetBytes(additionalChecksum)); } /// @@ -156,17 +149,17 @@ public static T CombineWith(this IDecorator decorator, params ulong[] addi /// /// The type of the . /// The to extend. - /// An array of bytes containing a checksum of the additional data the enclosed of the must represent. + /// A containing a checksum of the additional data the enclosed of the must represent. /// An updated instance of the enclosed of the . /// /// cannot be null. /// - public static T CombineWith(this IDecorator decorator, params byte[] additionalChecksum) where T : ChecksumBuilder + public static T CombineWith(this IDecorator decorator, byte[] additionalChecksum) where T : ChecksumBuilder { Validator.ThrowIfNull(decorator, nameof(decorator)); if (additionalChecksum == null) { return decorator.Inner; } if (additionalChecksum.Length == 0) { return decorator.Inner; } - decorator.Inner.AppendChecksum(additionalChecksum); + decorator.Inner.CombineWith(additionalChecksum); return decorator.Inner; } } diff --git a/src/Cuemon.Data.Integrity/FileChecksumOptions.cs b/src/Cuemon.Data.Integrity/FileChecksumOptions.cs index 7d8946b92..9f4b8eaf3 100644 --- a/src/Cuemon.Data.Integrity/FileChecksumOptions.cs +++ b/src/Cuemon.Data.Integrity/FileChecksumOptions.cs @@ -1,6 +1,5 @@ using System.IO; using Cuemon.IO; -using Cuemon.Security.Cryptography; namespace Cuemon.Data.Integrity { @@ -20,10 +19,6 @@ public class FileChecksumOptions : FileInfoOptions /// Initial Value /// /// - /// - /// - /// - /// /// /// /// @@ -31,15 +26,9 @@ public class FileChecksumOptions : FileInfoOptions /// public FileChecksumOptions() { - Algorithm = CryptoAlgorithm.Md5; Method = EntityDataIntegrityMethod.Unaltered; } - /// - /// Gets or sets the hash algorithm to use for the checksum computation. - /// - /// The hash algorithm to use for the checksum computation. - public CryptoAlgorithm Algorithm { get; set; } /// /// Gets an enumeration value of indicating how a checksum is generated. diff --git a/src/Cuemon.Data.Integrity/IDataIntegrity.cs b/src/Cuemon.Data.Integrity/IDataIntegrity.cs index ffa40808c..0df23fd2e 100644 --- a/src/Cuemon.Data.Integrity/IDataIntegrity.cs +++ b/src/Cuemon.Data.Integrity/IDataIntegrity.cs @@ -1,4 +1,4 @@ -using Cuemon.Security.Cryptography; +using Cuemon.Security; namespace Cuemon.Data.Integrity { diff --git a/src/Cuemon.Data.Integrity/IEntityDataIntegrity.cs b/src/Cuemon.Data.Integrity/IEntityDataIntegrity.cs index 3e2b70e1e..77d65c325 100644 --- a/src/Cuemon.Data.Integrity/IEntityDataIntegrity.cs +++ b/src/Cuemon.Data.Integrity/IEntityDataIntegrity.cs @@ -3,12 +3,13 @@ /// /// An interface that represents the integrity od data that is normally associated with an entity/resource. /// + /// public interface IEntityDataIntegrity : IDataIntegrity { /// /// Gets the validation strength of the integrity of this resource. /// /// The validation strength of the integrity of this resource. - EntityDataIntegrityStrength Validation { get; } + EntityDataIntegrityValidation Validation { get; } } } \ No newline at end of file diff --git a/src/Cuemon.Data.Integrity/IEntityData.cs b/src/Cuemon.Data.Integrity/IEntityInfo.cs similarity index 83% rename from src/Cuemon.Data.Integrity/IEntityData.cs rename to src/Cuemon.Data.Integrity/IEntityInfo.cs index 088b37ed3..7c128a4e7 100644 --- a/src/Cuemon.Data.Integrity/IEntityData.cs +++ b/src/Cuemon.Data.Integrity/IEntityInfo.cs @@ -5,7 +5,7 @@ /// /// /// - public interface IEntityData : IEntityDataTimestamp, IEntityDataIntegrity + public interface IEntityInfo : IEntityDataTimestamp, IEntityDataIntegrity { } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/CacheableObjectResultExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/CacheableObjectResultExtensions.cs index 89387ca7e..4caaea804 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/CacheableObjectResultExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/CacheableObjectResultExtensions.cs @@ -53,7 +53,7 @@ public static ICacheableObjectResult MakeCacheable(this object instance, FuncAn implementation. /// /// - /// + /// /// /// /// @@ -109,7 +109,7 @@ public static ICacheableObjectResult MakeCacheable(this T instance, FuncAn implementation. /// /// - /// + /// /// /// /// diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBusting.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBusting.cs index d37c6e999..856cd9966 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBusting.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBusting.cs @@ -1,5 +1,6 @@ using System; using Cuemon.Extensions.Data.Integrity; +using Cuemon.Security.Cryptography; using Microsoft.Extensions.Options; namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration @@ -17,7 +18,10 @@ public sealed class AssemblyCacheBusting : CacheBusting public AssemblyCacheBusting(IOptions setup) { var options = setup.Value; - var version = options.Assembly?.GetCacheValidator(options.ReadByteForByteChecksum, o => o.Algorithm = options.Algorithm).Checksum.ToHexadecimalString() ?? Guid.NewGuid().ToString("N"); // fallback to Guid.NewGuid + var version = options.Assembly?.GetCacheValidator(() => UnkeyedHashFactory.CreateCrypto(options.Algorithm), o => + { + if (options.ReadByteForByteChecksum) { o.BytesToRead = int.MaxValue; } + }).Checksum.ToHexadecimalString() ?? Guid.NewGuid().ToString("N"); // fallback to Guid.NewGuid Version = Decorator.Enclose(version).ToCasing(options.PreferredCasing); } diff --git a/src/Cuemon.Extensions.AspNetCore/Data/Integrity/CacheValidatorExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Data/Integrity/CacheValidatorExtensions.cs index c46f2a205..30fe1927c 100644 --- a/src/Cuemon.Extensions.AspNetCore/Data/Integrity/CacheValidatorExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Data/Integrity/CacheValidatorExtensions.cs @@ -16,7 +16,7 @@ public static class CacheValidatorExtensions public static EntityTagHeaderValue ToEntityTag(this CacheValidator validator) { Validator.ThrowIfNull(validator, nameof(validator)); - return validator.ToEntityTagHeaderValue(validator.Strength != EntityDataIntegrityStrength.Strong); + return validator.ToEntityTagHeaderValue(validator.Validation != EntityDataIntegrityValidation.Strong); } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Data.Integrity/AssemblyExtensions.cs b/src/Cuemon.Extensions.Data.Integrity/AssemblyExtensions.cs index 1fb2bc2f9..05fe491fa 100644 --- a/src/Cuemon.Extensions.Data.Integrity/AssemblyExtensions.cs +++ b/src/Cuemon.Extensions.Data.Integrity/AssemblyExtensions.cs @@ -2,7 +2,7 @@ using System.IO; using System.Reflection; using Cuemon.Data.Integrity; -using Cuemon.IO; +using Cuemon.Security; namespace Cuemon.Extensions.Data.Integrity { @@ -15,20 +15,17 @@ public static class AssemblyExtensions /// Returns a from the specified . /// /// The assembly to resolve a from. - /// true to read the byte-for-byte to promote a strong integrity checksum; false to read common properties of the for a weak (but reliable) integrity checksum. - /// The which may be configured. - /// A that fully represents the integrity of the specified . - public static CacheValidator GetCacheValidator(this Assembly assembly, bool readByteForByteChecksum = false, Action setup = null) + /// The function delegate that is invoked to produce the . + /// The which may be configured. + /// A that represents the integrity of the specified . + public static CacheValidator GetCacheValidator(this Assembly assembly, Func hashFactory = null, Action setup = null) { if (assembly == null || assembly.IsDynamic) { return CacheValidator.Default; } var assemblyHashCode64 = Generate.HashCode64(assembly.FullName); var assemblyLocation = assembly.Location; - return string.IsNullOrEmpty(assemblyLocation) ? new CacheValidator(DateTime.MinValue, DateTime.MaxValue, assemblyHashCode64, setup) : new FileInfo(assemblyLocation).GetCacheValidator(Patterns.ConfigureExchange(setup, (cvo, fco) => - { - fco.BytesToRead = readByteForByteChecksum ? int.MaxValue : 0; - fco.Algorithm = cvo.Algorithm; - fco.Method = cvo.Method; - })).CombineWith(assemblyHashCode64); + return string.IsNullOrEmpty(assemblyLocation) + ? new CacheValidator(new EntityInfo(DateTime.MinValue, DateTime.MaxValue, Convertible.GetBytes(assemblyHashCode64)), hashFactory) + : new FileInfo(assemblyLocation).GetCacheValidator(hashFactory, setup).CombineWith(assemblyHashCode64); } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Data.Integrity/ChecksumBuilderExtensions.cs b/src/Cuemon.Extensions.Data.Integrity/ChecksumBuilderExtensions.cs index 0a2df9302..3b98a4dbd 100644 --- a/src/Cuemon.Extensions.Data.Integrity/ChecksumBuilderExtensions.cs +++ b/src/Cuemon.Extensions.Data.Integrity/ChecksumBuilderExtensions.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using System.Linq; -using Cuemon.Data.Integrity; +using Cuemon.Data.Integrity; namespace Cuemon.Extensions.Data.Integrity { @@ -13,128 +11,120 @@ public static class ChecksumBuilderExtensions /// Combines the to the representation of this instance. /// /// The type of the . - /// An instance of a . - /// A array that contains zero or more checksum of the additional data this instance must represent. - /// An updated instance the specified of . - public static T CombineWith(this T cb, params double[] additionalChecksum) where T : ChecksumBuilder + /// The to extend. + /// A value containing a checksum of the additional data this instance must represent. + /// An updated instance of the specified of . + public static T CombineWith(this T cb, double additionalChecksum) where T : ChecksumBuilder { - return CombineWith(cb, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return Decorator.Enclose(cb).CombineWith(additionalChecksum); } /// /// Combines the to the representation of this instance. /// /// The type of the . - /// An instance of a . - /// An array that contains zero or more checksum of the additional data this instance must represent. - /// An updated instance the specified of . - public static T CombineWith(this T cb, params short[] additionalChecksum) where T : ChecksumBuilder + /// The to extend. + /// An value containing a checksum of the additional data this instance must represent. + /// An updated instance of the specified of . + public static T CombineWith(this T cb, short additionalChecksum) where T : ChecksumBuilder { - return CombineWith(cb, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return Decorator.Enclose(cb).CombineWith(additionalChecksum); } /// /// Combines the to the representation of this instance. /// /// The type of the . - /// An instance of a . - /// A array that contains zero or more checksum of the additional data this instance must represent. - /// An updated instance the specified of . - public static T CombineWith(this T cb, params string[] additionalChecksum) where T : ChecksumBuilder + /// The to extend. + /// A value containing a checksum of the additional data this instance must represent. + /// An updated instance of the specified of . + public static T CombineWith(this T cb, string additionalChecksum) where T : ChecksumBuilder { - List result = new List(); - for (int i = 0; i < additionalChecksum.Length; i++) - { - result.Add(Generate.HashCode64(additionalChecksum[i])); - } - return CombineWith(cb, result.ToArray()); + return Decorator.Enclose(cb).CombineWith(additionalChecksum); } /// /// Combines the to the representation of this instance. /// /// The type of the . - /// An instance of a . - /// An array that contains zero or more checksum of the additional data this instance must represent. - /// An updated instance the specified of . - public static T CombineWith(this T cb, params int[] additionalChecksum) where T : ChecksumBuilder + /// The to extend. + /// An value containing a checksum of the additional data this instance must represent. + /// An updated instance of the specified of . + public static T CombineWith(this T cb, int additionalChecksum) where T : ChecksumBuilder { - return CombineWith(cb, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return Decorator.Enclose(cb).CombineWith(additionalChecksum); } /// /// Combines the to the representation of this instance. /// /// The type of the . - /// An instance of a . - /// An array that contains zero or more checksum of the additional data this instance must represent. - /// An updated instance the specified of . - public static T CombineWith(this T cb, params long[] additionalChecksum) where T : ChecksumBuilder + /// The to extend. + /// An value containing a checksum of the additional data this instance must represent. + /// An updated instance of the specified of . + public static T CombineWith(this T cb, long additionalChecksum) where T : ChecksumBuilder { - return CombineWith(cb, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return Decorator.Enclose(cb).CombineWith(additionalChecksum); } /// /// Combines the to the representation of this instance. /// /// The type of the . - /// An instance of a . - /// A array that contains zero or more checksum of the additional data this instance must represent. - /// An updated instance the specified of . - public static T CombineWith(this T cb, params float[] additionalChecksum) where T : ChecksumBuilder + /// The to extend. + /// A value containing a checksum of the additional data this instance must represent. + /// An updated instance of the specified of . + public static T CombineWith(this T cb, float additionalChecksum) where T : ChecksumBuilder { - return CombineWith(cb, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return Decorator.Enclose(cb).CombineWith(additionalChecksum); } /// /// Combines the to the representation of this instance. /// /// The type of the . - /// An instance of a . - /// An array that contains zero or more checksum of the additional data this instance must represent. - /// An updated instance the specified of . - public static T CombineWith(this T cb, params ushort[] additionalChecksum) where T : ChecksumBuilder + /// The to extend. + /// An value containing a checksum of the additional data this instance must represent. + /// An updated instance of the specified of . + public static T CombineWith(this T cb, ushort additionalChecksum) where T : ChecksumBuilder { - return CombineWith(cb, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return Decorator.Enclose(cb).CombineWith(additionalChecksum); } /// /// Combines the to the representation of this instance. /// /// The type of the . - /// An instance of a . - /// An array that contains zero or more checksum of the additional data this instance must represent. - /// An updated instance the specified of . - public static T CombineWith(this T cb, params uint[] additionalChecksum) where T : ChecksumBuilder + /// The to extend. + /// An value containing a checksum of the additional data this instance must represent. + /// An updated instance of the specified of . + public static T CombineWith(this T cb, uint additionalChecksum) where T : ChecksumBuilder { - return CombineWith(cb, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return Decorator.Enclose(cb).CombineWith(additionalChecksum); } /// /// Combines the to the representation of this instance. /// /// The type of the . - /// An instance of a . - /// An array that contains zero or more checksum of the additional data this instance must represent. - /// An updated instance the specified of . - public static T CombineWith(this T cb, params ulong[] additionalChecksum) where T : ChecksumBuilder + /// The to extend. + /// An value containing a checksum of the additional data this instance must represent. + /// An updated instance of the specified of . + public static T CombineWith(this T cb, ulong additionalChecksum) where T : ChecksumBuilder { - return CombineWith(cb, additionalChecksum?.SelectMany(x => Convertible.GetBytes(x)).ToArray()); + return Decorator.Enclose(cb).CombineWith(additionalChecksum); } /// /// Combines the to the representation of this instance. /// /// The type of the . - /// An instance of a . + /// The to extend. /// An array of bytes containing a checksum of the additional data this instance must represent. - /// An updated instance the specified of . - public static T CombineWith(this T cb, params byte[] additionalChecksum) where T : ChecksumBuilder + /// An updated instance of the specified of . + public static T CombineWith(this T cb, byte[] additionalChecksum) where T : ChecksumBuilder { - if (additionalChecksum == null) { return cb; } - if (additionalChecksum.Length == 0) { return cb; } - cb.AppendChecksum(additionalChecksum); - return cb; + return Decorator.Enclose(cb).CombineWith(additionalChecksum); } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Data.Integrity/DateTimeExtensions.cs b/src/Cuemon.Extensions.Data.Integrity/DateTimeExtensions.cs index c00a44293..314eb3b02 100644 --- a/src/Cuemon.Extensions.Data.Integrity/DateTimeExtensions.cs +++ b/src/Cuemon.Extensions.Data.Integrity/DateTimeExtensions.cs @@ -1,5 +1,6 @@ using System; using Cuemon.Data.Integrity; +using Cuemon.Security; namespace Cuemon.Extensions.Data.Integrity { @@ -8,145 +9,18 @@ namespace Cuemon.Extensions.Data.Integrity /// public static class DateTimeExtensions { - /// - /// Returns a from the specified parameters. - /// - /// A value for when data this represents was first created. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, Action setup = null) - { - return new CacheValidator(created, setup); - } - /// /// Returns a from the specified parameters. /// /// A value for when data this represents was first created. /// A value for when data this represents was last modified. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, Action setup = null) + /// The function delegate that is invoked to produce the . Default is . + /// A enumeration value that indicates how a checksum is manipulated. Default is . + /// A that represents the integrity of the specified parameters. + public static CacheValidator GetCacheValidator(this DateTime created, DateTime? modified = null, Func hashFactory = null, EntityDataIntegrityMethod method = EntityDataIntegrityMethod.Unaltered) { - return new CacheValidator(created, modified, setup); - } - - /// - /// Returns a from the specified parameters. - /// - /// A value for when data this represents was first created. - /// A value for when data this represents was last modified. - /// A value containing a byte-for-byte checksum of the data this represents. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, double checksum, Action setup = null) - { - return new CacheValidator(created, modified, checksum, setup); - } - - - /// - /// Returns a from the specified parameters. - /// - /// A value for when data this represents was first created. - /// A value for when data this represents was last modified. - /// A value containing a byte-for-byte checksum of the data this represents. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, short checksum, Action setup = null) - { - return new CacheValidator(created, modified, checksum, setup); - } - - /// - /// Returns a from the specified parameters. - /// - /// A value for when data this represents was first created. - /// A value for when data this represents was last modified. - /// A value containing a byte-for-byte checksum of the data this represents. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, string checksum, Action setup = null) - { - return new CacheValidator(created, modified, checksum, setup); - } - - /// - /// Returns a from the specified parameters. - /// - /// A value for when data this represents was first created. - /// A value for when data this represents was last modified. - /// A value containing a byte-for-byte checksum of the data this represents. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, int checksum, Action setup = null) - { - return new CacheValidator(created, modified, checksum, setup); - } - - /// - /// Returns a from the specified parameters. - /// - /// A value for when data this represents was first created. - /// A value for when data this represents was last modified. - /// A value containing a byte-for-byte checksum of the data this represents. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, long checksum, Action setup = null) - { - return new CacheValidator(created, modified, checksum, setup); - } - - /// - /// Returns a from the specified parameters. - /// - /// A value for when data this represents was first created. - /// A value for when data this represents was last modified. - /// A value containing a byte-for-byte checksum of the data this represents. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, float checksum, Action setup = null) - { - return new CacheValidator(created, modified, checksum, setup); - } - - /// - /// Returns a from the specified parameters. - /// - /// A value for when data this represents was first created. - /// A value for when data this represents was last modified. - /// A value containing a byte-for-byte checksum of the data this represents. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, ushort checksum, Action setup = null) - { - return new CacheValidator(created, modified, checksum, setup); - } - - /// - /// Returns a from the specified parameters. - /// - /// A value for when data this represents was first created. - /// A value for when data this represents was last modified. - /// A value containing a byte-for-byte checksum of the data this represents. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, uint checksum, Action setup = null) - { - return new CacheValidator(created, modified, checksum, setup); - } - - /// - /// Returns a from the specified parameters. - /// - /// A value for when data this represents was first created. - /// A value for when data this represents was last modified. - /// A value containing a byte-for-byte checksum of the data this represents. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, ulong checksum, Action setup = null) - { - return new CacheValidator(created, modified, checksum, setup); + if (hashFactory == null) { hashFactory = () => HashFactory.CreateFnv128(); } + return new CacheValidator(new EntityInfo(created, modified), hashFactory, method); } /// @@ -155,11 +29,14 @@ public static CacheValidator GetCacheValidator(this DateTime created, DateTime m /// A value for when data this represents was first created. /// A value for when data this represents was last modified. /// An array of bytes containing a checksum of the data this represents. - /// The which need to be configured. - /// A that fully represents the integrity of the specified parameters. - public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, byte[] checksum, Action setup = null) - { - return new CacheValidator(created, modified, checksum, setup); + /// A enumeration value that indicates the validation strength of the specified . Default is . + /// The function delegate that is invoked to produce the . Default is . + /// A enumeration value that indicates how a checksum is manipulated. Default is . + /// A that represents the integrity of the specified parameters. + public static CacheValidator GetCacheValidator(this DateTime created, DateTime modified, byte[] checksum, EntityDataIntegrityValidation validation = EntityDataIntegrityValidation.Weak, Func hashFactory = null, EntityDataIntegrityMethod method = EntityDataIntegrityMethod.Unaltered) + { + if (hashFactory == null) { hashFactory = () => HashFactory.CreateFnv128(); } + return new CacheValidator(new EntityInfo(created, modified, checksum, validation), hashFactory, method); } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Data.Integrity/FileInfoExtensions.cs b/src/Cuemon.Extensions.Data.Integrity/FileInfoExtensions.cs index 9d28e3498..f6efa7137 100644 --- a/src/Cuemon.Extensions.Data.Integrity/FileInfoExtensions.cs +++ b/src/Cuemon.Extensions.Data.Integrity/FileInfoExtensions.cs @@ -1,6 +1,7 @@ using System; using System.IO; using Cuemon.Data.Integrity; +using Cuemon.Security; namespace Cuemon.Extensions.Data.Integrity { @@ -13,18 +14,19 @@ public static class FileInfoExtensions /// Returns a from the specified . /// /// The to extend. + /// The function delegate that is invoked to produce the . Default is . /// The which may be configured. /// A that represents either a weak, medium or strong integrity check of the specified . /// /// is null. /// /// Should the specified trigger any sort of exception, a is returned. - public static CacheValidator GetCacheValidator(this FileInfo file, Action setup = null) + public static CacheValidator GetCacheValidator(this FileInfo file, Func hashFactory = null, Action setup = null) { Validator.ThrowIfNull(file, nameof(file)); try { - return CacheValidatorFactory.CreateValidator(file, setup); + return CacheValidatorFactory.CreateValidator(file, hashFactory, setup); } catch (Exception) { diff --git a/src/Cuemon.Extensions.Data.Integrity/GlobalSuppressions.cs b/src/Cuemon.Extensions.Data.Integrity/GlobalSuppressions.cs deleted file mode 100644 index 5a887d01a..000000000 --- a/src/Cuemon.Extensions.Data.Integrity/GlobalSuppressions.cs +++ /dev/null @@ -1,8 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Major Code Smell", "S3358:Ternary operators should not be nested", Justification = "Not difficult to understand.", Scope = "member", Target = "~M:Cuemon.Extensions.Integrity.AssemblyExtensions.GetCacheValidator(System.Reflection.Assembly,System.Boolean,System.Action{Cuemon.Integrity.CacheValidatorOptions})~Cuemon.Integrity.CacheValidator")] \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/CacheableObjectTest.cs b/test/Cuemon.AspNetCore.Mvc.Tests/CacheableObjectTest.cs index 34d862ffb..c6360a3fc 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/CacheableObjectTest.cs +++ b/test/Cuemon.AspNetCore.Mvc.Tests/CacheableObjectTest.cs @@ -35,7 +35,7 @@ public void CreateCacheableObject_ShouldHaveICacheableIntegrityImplementation_Wh Assert.IsAssignableFrom(cor); if (cor.Value is IEntityDataIntegrity integrity) { - Assert.True(integrity.Validation == EntityDataIntegrityStrength.Strong); + Assert.True(integrity.Validation == EntityDataIntegrityValidation.Strong); Assert.True(integrity.Checksum.GetBytes() == orBytes); } Assert.Equal(cor.Value, or); @@ -47,12 +47,12 @@ public void CreateCacheableObject_ShouldHaveICacheableEntityImplementation_WhenC var or = Generate.RandomString(2048); var orBytes = Convertible.GetBytes(or); var cor = CacheableObjectFactory.CreateCacheableObjectResult(or, () => DateTime.MinValue, () => orBytes, () => DateTime.MaxValue, () => false); - Assert.IsAssignableFrom(cor); - if (cor.Value is IEntityData entity) + Assert.IsAssignableFrom(cor); + if (cor.Value is IEntityInfo entity) { Assert.True(entity.Created == DateTime.MinValue); Assert.True(entity.Modified == DateTime.MinValue); - Assert.True(entity.Validation == EntityDataIntegrityStrength.Strong); + Assert.True(entity.Validation == EntityDataIntegrityValidation.Strong); Assert.True(entity.Checksum.GetBytes() == orBytes); } Assert.Equal(cor.Value, or); diff --git a/test/Cuemon.Extensions.Data.Integrity.Tests/AssemblyExtensionsTest.cs b/test/Cuemon.Extensions.Data.Integrity.Tests/AssemblyExtensionsTest.cs index 97b560636..f509c033f 100644 --- a/test/Cuemon.Extensions.Data.Integrity.Tests/AssemblyExtensionsTest.cs +++ b/test/Cuemon.Extensions.Data.Integrity.Tests/AssemblyExtensionsTest.cs @@ -16,10 +16,15 @@ public void GetCacheValidator_ShouldHaveStrongIntegrityChecksum() { var a = typeof(AssemblyExtensionsTest).Assembly; - var cv = a.GetCacheValidator(); - Assert.Equal(EntityDataIntegrityStrength.Strong, cv.Strength); - Assert.NotEqual(cv.ToString(), CacheValidator.Default.ToString()); - TestOutput.WriteLine(cv.ToString()); + var cv1 = a.GetCacheValidator(); + Assert.Equal(EntityDataIntegrityValidation.Weak, cv1.Validation); + var cv2 = a.GetCacheValidator(setup: o => o.BytesToRead = 400); + Assert.Equal(EntityDataIntegrityValidation.Strong, cv2.Validation); + Assert.NotEqual(cv1.ToString(), CacheValidator.Default.ToString()); + Assert.NotEqual(cv2.ToString(), CacheValidator.Default.ToString()); + Assert.NotEqual(cv1.ToString(), cv2.ToString()); + TestOutput.WriteLine(cv1.ToString()); + TestOutput.WriteLine(cv2.ToString()); } } } \ No newline at end of file diff --git a/test/Cuemon.Extensions.Data.Integrity.Tests/DateTimeExtensionsTest.cs b/test/Cuemon.Extensions.Data.Integrity.Tests/DateTimeExtensionsTest.cs index 7cc28f3a2..48068cb45 100644 --- a/test/Cuemon.Extensions.Data.Integrity.Tests/DateTimeExtensionsTest.cs +++ b/test/Cuemon.Extensions.Data.Integrity.Tests/DateTimeExtensionsTest.cs @@ -13,32 +13,42 @@ public DateTimeExtensionsTest(ITestOutputHelper output) : base(output) } [Fact] - public void GetCacheValidator_ShouldHaveNoneIntegrityChecksum() + public void GetCacheValidator_UseDefaultMethod_ShouldHaveUnspecifiedIntegrityValidation() { var dt = DateTime.UnixEpoch.GetCacheValidator(); - var expected = "d41d8cd98f00b204e9800998ecf8427e"; + var expected = "6c62272e07bb014262b821756295c58d"; Assert.Equal(expected, dt.ToString()); - Assert.Equal(EntityDataIntegrityStrength.Unspecified, dt.Strength); + Assert.Equal(EntityDataIntegrityValidation.Unspecified, dt.Validation); TestOutput.WriteLine(dt.ToString()); } [Fact] - public void GetCacheValidator_ShouldHaveWeakIntegrityChecksum() + public void GetCacheValidator_UseTimestampMethod_ShouldHaveUnspecifiedIntegrityValidation() { - var dt = DateTime.UnixEpoch.GetCacheValidator(DateTime.UnixEpoch.AddDays(7), o => o.Method = EntityDataIntegrityMethod.Timestamp); - var expected = "15f5959e2cf903cf2bf3dc93d4c89cb3"; + var dt = DateTime.UnixEpoch.GetCacheValidator(DateTime.UnixEpoch.AddDays(7), method: EntityDataIntegrityMethod.Timestamp); + var expected = "1192c6957365995ad3a62bff3cf1b3ad"; Assert.Equal(expected, dt.ToString()); - Assert.Equal(EntityDataIntegrityStrength.Weak, dt.Strength); + Assert.Equal(EntityDataIntegrityValidation.Unspecified, dt.Validation); TestOutput.WriteLine(dt.ToString()); } [Fact] - public void GetCacheValidator_ShouldHaveStrongIntegrityChecksum() + public void GetCacheValidator_UseDefaultMethod_ShouldHaveWeakIntegrityValidation() { - var dt = DateTime.UnixEpoch.GetCacheValidator(DateTime.UnixEpoch.AddDays(7), 1234567890); - var expected = "bbb1f04aceb3b6903f7f364a25501220"; + var dt = DateTime.UnixEpoch.GetCacheValidator(DateTime.UnixEpoch.AddDays(7), Convertible.GetBytes(1234567890)); + var expected = "65567817ef757277b806e833c0139a60"; Assert.Equal(expected, dt.ToString()); - Assert.Equal(EntityDataIntegrityStrength.Strong, dt.Strength); + Assert.Equal(EntityDataIntegrityValidation.Weak, dt.Validation); + TestOutput.WriteLine(dt.ToString()); + } + + [Fact] + public void GetCacheValidator_UseDefaultMethod_ShouldHaveStrongIntegrityValidation() + { + var dt = DateTime.UnixEpoch.GetCacheValidator(DateTime.UnixEpoch.AddDays(7), Convertible.GetBytes(1234567890), EntityDataIntegrityValidation.Strong); + var expected = "65567817ef757277b806e833c0139a60"; + Assert.Equal(expected, dt.ToString()); + Assert.Equal(EntityDataIntegrityValidation.Strong, dt.Validation); TestOutput.WriteLine(dt.ToString()); } } From 8a29702181fca4361fc972b43302ccf2a3e50020 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 17:04:09 +0200 Subject: [PATCH 103/385] Consequence changes of refactoring Cuemon.Security.Cryptography to its own assembly. --- Cuemon.sln | 14 +++ .../AssemblyCacheBustingOptions.cs | 4 +- .../AssemblyDecoratorExtensionsTest.cs | 2 +- .../{Cryptography => }/HashFactoryTest.cs | 112 +----------------- .../AesCryptorTest.cs | 0 .../Cuemon.Security.Cryptography.Tests.csproj | 11 ++ .../KeyedHashFactoryTest.cs | 58 +++++++++ .../UnkeyedHashFactoryTest.cs | 76 ++++++++++++ 8 files changed, 164 insertions(+), 113 deletions(-) rename test/Cuemon.Core.Tests/Security/{Cryptography => }/HashFactoryTest.cs (62%) rename test/{Cuemon.Core.Tests/Security/Cryptography => Cuemon.Security.Cryptography.Tests}/AesCryptorTest.cs (100%) create mode 100644 test/Cuemon.Security.Cryptography.Tests/Cuemon.Security.Cryptography.Tests.csproj create mode 100644 test/Cuemon.Security.Cryptography.Tests/KeyedHashFactoryTest.cs create mode 100644 test/Cuemon.Security.Cryptography.Tests/UnkeyedHashFactoryTest.cs diff --git a/Cuemon.sln b/Cuemon.sln index 0afcf8198..849d431d7 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -105,6 +105,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Diagnostics", "src\C EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Diagnostics.Tests", "test\Cuemon.Diagnostics.Tests\Cuemon.Diagnostics.Tests.csproj", "{06559CB0-899C-4B48-AFB8-633CBF97A766}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Security.Cryptography", "src\Cuemon.Security.Cryptography\Cuemon.Security.Cryptography.csproj", "{1B0BDF91-E7C7-4CB4-A39D-E1A5374C5602}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Security.Cryptography.Tests", "test\Cuemon.Security.Cryptography.Tests\Cuemon.Security.Cryptography.Tests.csproj", "{5D67081C-4458-41AA-A1F5-FAC974D29FDF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -307,6 +311,14 @@ Global {06559CB0-899C-4B48-AFB8-633CBF97A766}.Debug|Any CPU.Build.0 = Debug|Any CPU {06559CB0-899C-4B48-AFB8-633CBF97A766}.Release|Any CPU.ActiveCfg = Release|Any CPU {06559CB0-899C-4B48-AFB8-633CBF97A766}.Release|Any CPU.Build.0 = Release|Any CPU + {1B0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.Build.0 = Release|Any CPU + {5D67081C-4458-41AA-A1F5-FAC974D29FDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D67081C-4458-41AA-A1F5-FAC974D29FDF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D67081C-4458-41AA-A1F5-FAC974D29FDF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D67081C-4458-41AA-A1F5-FAC974D29FDF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -361,6 +373,8 @@ Global {190BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} {1A0BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} {06559CB0-899C-4B48-AFB8-633CBF97A766} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} + {1B0BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} + {5D67081C-4458-41AA-A1F5-FAC974D29FDF} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2A848386-B682-4F6D-8254-B5F6247C3054} diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs index ce5337805..c598e648f 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs @@ -36,7 +36,7 @@ public class AssemblyCacheBustingOptions : CacheBustingOptions public AssemblyCacheBustingOptions() { Assembly = Assembly.GetEntryAssembly(); - Algorithm = CryptoAlgorithm.Md5; + Algorithm = UnkeyedCryptoAlgorithm.Md5; ReadByteForByteChecksum = false; } @@ -50,7 +50,7 @@ public AssemblyCacheBustingOptions() /// Gets or sets the hash algorithm to use for the computation of . /// /// The hash algorithm to use for the computation of . - public CryptoAlgorithm Algorithm { get; set; } + public UnkeyedCryptoAlgorithm Algorithm { get; set; } /// /// Gets or sets a value indicating whether the will be read byte-for-byte when computing the checksum. diff --git a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs index 226b3899c..30d13fbf3 100644 --- a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs @@ -36,7 +36,7 @@ public void GetTypes_ShouldReturnAllTypesFromCuemonCore() var disposableTypesCount = Decorator.Enclose(disposableTypes).Inner.Count(); var configurationTypesCount = Decorator.Enclose(configurationTypes).Inner.Count(); - Assert.InRange(allTypesCount, 485, 490); // range because of tooling on CI adding dynamic types + Assert.InRange(allTypesCount, 465, 470); // range because of tooling on CI adding dynamic types Assert.Equal(4, disposableTypesCount); Assert.Equal(2, configurationTypesCount); } diff --git a/test/Cuemon.Core.Tests/Security/Cryptography/HashFactoryTest.cs b/test/Cuemon.Core.Tests/Security/HashFactoryTest.cs similarity index 62% rename from test/Cuemon.Core.Tests/Security/Cryptography/HashFactoryTest.cs rename to test/Cuemon.Core.Tests/Security/HashFactoryTest.cs index b642372ce..f39c64b2b 100644 --- a/test/Cuemon.Core.Tests/Security/Cryptography/HashFactoryTest.cs +++ b/test/Cuemon.Core.Tests/Security/HashFactoryTest.cs @@ -1,10 +1,9 @@ -using System; -using System.Text; +using System.Text; using Cuemon.Extensions.Xunit; using Xunit; using Xunit.Abstractions; -namespace Cuemon.Security.Cryptography +namespace Cuemon.Security { public class HashFactoryTest : Test { @@ -139,113 +138,6 @@ public void CreateCrc_Crc32CdRomEdc_ShouldBeValidHashResult() Assert.Equal("D9B8B5E9", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString().ToUpper()); } - [Fact] - public void CreateHmacCryptoMd5_ShouldBeValidHashResult() - { - var h = HashFactory.CreateHmacCryptoMd5(Decorator.Enclose("unittest").ToByteArray()); - Assert.Equal("d0ee1decd115feac4608976f28e19e10", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); - Assert.Equal("34bdd10e5c71eb6860294d19f2db8233", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("70adffcc026ac757db66de1bbb005e04", h.ComputeHash("what-a-feeling-#-¤-%-!-cover-from-dj-bobo-128379539285784289529893278278173981247983251311").ToHexadecimalString()); - } - - [Fact] - public void CreateHmacCryptoSha1_ShouldBeValidHashResult() - { - var h = HashFactory.CreateHmacCryptoSha1(Decorator.Enclose("unittest").ToByteArray()); - Assert.Equal("0c7d9d4461c66d2d6eafef7211689abd20b28b39", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); - Assert.Equal("62ea258e7e4d0522fe5e2b06e1ab600051c542c4", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("bae9d2e09c9b5f65f9a7a3c5a55748b180ee65c1", h.ComputeHash("what-a-feeling-#-¤-%-!-cover-from-dj-bobo-128379539285784289529893278278173981247983251311").ToHexadecimalString()); - } - - [Fact] - public void CreateHmacCryptoSha256_ShouldBeValidHashResult() - { - var h = HashFactory.CreateHmacCryptoSha256(Decorator.Enclose("unittest").ToByteArray()); - Assert.Equal("c06272f221bcbfc6783c059e85d7ecdb412e80f2c2b704c27885d64d984d95e3", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); - Assert.Equal("1fd4ff25f71fa49435bc6996bbbaeeb9abe2f47ed3256b8a9f44b8eea49a23b5", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("38367544d02183414967e4669cd298ca6d644db6c6bfa903afae497c6ab300fa", h.ComputeHash("what-a-feeling-#-¤-%-!-cover-from-dj-bobo-128379539285784289529893278278173981247983251311").ToHexadecimalString()); - } - - [Fact] - public void CreateHmacCryptoSha384_ShouldBeValidHashResult() - { - var h = HashFactory.CreateHmacCryptoSha384(Decorator.Enclose("unittest").ToByteArray()); - Assert.Equal("87d90ce62441fd39f81d327fb4d3a9902d80a0d8312a961fdfc9b1bcf756afd77f0f0596d30a868a3cda1c49abf1a3ee", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); - Assert.Equal("7a90151a628de76431d11cf01200a7213f0dc491f7677aafbcd232e512521ca1ac3771d2b6204fa1ab6b54cf085ba2d3", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("3b00f1f207819422761a22b918c69a3e16ae0d5268c01ad331311184b6a809b927b324f75d8cb8f1cee0f849382558bd", h.ComputeHash("what-a-feeling-#-¤-%-!-cover-from-dj-bobo-128379539285784289529893278278173981247983251311").ToHexadecimalString()); - } - - [Fact] - public void CreateHmacCryptoSha512_ShouldBeValidHashResult() - { - var h = HashFactory.CreateHmacCryptoSha512(Decorator.Enclose("unittest").ToByteArray()); - Assert.Equal("db0087dde1ad907c11037243bf700a9f08430899edfc19fe1fadb7d4e6846182fa0100762cf42c64828f1bfe41493275b98f5c0c1a80300656e0d97f9f1d892e", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); - Assert.Equal("7b674e1138c2ee91828fcf5318d356e72a9c9e4e533234181640b0d1728f305db656bb7e57c95e101c0efbed9290454ef0c37f514c8dfb85f003c483bf0f236a", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("33520bc89652184cf5a17600a78a513db44a0b7eb1fb001525ab0e77c94afcb45b7878b259cd37896ddbbc5dff76d2cbb8f57eaf44c4f4aac77695fe8fe28992", h.ComputeHash("what-a-feeling-#-¤-%-!-cover-from-dj-bobo-128379539285784289529893278278173981247983251311").ToHexadecimalString()); - } - - [Fact] - public void CreateCryptoSha512_ShouldBeValidHashResult() - { - var h = HashFactory.CreateCryptoSha512(); - Assert.Equal("1e07be23c26a86ea37ea810c8ec7809352515a970e9253c26f536cfc7a9996c45c8370583e0a78fa4a90041d71a4ceab7423f19c71b9d5a3e01249f0bebd5894", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); - Assert.Equal("bb96c2fc40d2d54617d6f276febe571f623a8dadf0b734855299b0e107fda32cf6b69f2da32b36445d73690b93cbd0f7bfc20e0f7f28553d2a4428f23b716e90", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("0b6cbac838dfe7f47ea1bd0df00ec282fdf45510c92161072ccfb84035390c4da743d9c3b954eaa1b0f86fc9861b23cc6c8667ab232c11c686432ebb5c8c3f27", h.ComputeHash(Guid.Empty.ToByteArray()).ToHexadecimalString()); - } - - [Fact] - public void CreateCryptoSha384_ShouldBeValidHashResult() - { - var h = HashFactory.CreateCryptoSha384(); - Assert.Equal("1761336e3f7cbfe51deb137f026f89e01a448e3b1fafa64039c1464ee8732f11a5341a6f41e0c202294736ed64db1a84", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); - Assert.Equal("90ae531f24e48697904a4d0286f354c50a350ebb6c2b9efcb22f71c96ceaeffc11c6095e9ca0df0ec30bf685dcf2e5e5", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("7210af19145ec2a8e250a7fe8e9eeeac1301e524daab82366c36be614dc35402a289101e48cad61c45337f2f32c14fdc", h.ComputeHash(VerticalDirection.Up).ToHexadecimalString()); - } - - [Fact] - public void CreateCryptoSha256_ShouldBeValidHashResult() - { - var h = HashFactory.CreateCryptoSha256(); - Assert.Equal("db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); - Assert.Equal("db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0", h.ComputeHash(Decorator.Enclose(Alphanumeric.LettersAndNumbers).ToStream()).ToHexadecimalString()); - Assert.Equal("84d89877f0d4041efb6bf91a16f0248f2fd573e6af05c19f96bedb9f882f7882", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("53ab3a50f51855beeae9721ab68656312c7f105b9b34bbfa97875dbfda72dbc6", h.ComputeHash(DateTime.UnixEpoch).ToHexadecimalString()); - Assert.Equal("1f1a24c833be74a0f4f99007aa70a51e2456e41f745a5628721ea2b8e1c07641", h.ComputeHash(213, "fdfsfsf", 9999).ToHexadecimalString()); - } - - [Fact] - public void CreateCryptoSha1_ShouldBeValidHashResult() - { - var h = HashFactory.CreateCryptoSha1(); - Assert.Equal("761c457bf73b14d27e9e9265c46f4b4dda11f940", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); - Assert.Equal("761c457bf73b14d27e9e9265c46f4b4dda11f940", h.ComputeHash(Decorator.Enclose(Alphanumeric.LettersAndNumbers).ToStream()).ToHexadecimalString()); - Assert.Equal("87acec17cd9dcd20a716cc2cf67417b71c8a7016", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("485bad9954c874513b8ff7b6d5ea459ac65dd075", h.ComputeHash(decimal.MinValue).ToHexadecimalString()); - Assert.Equal("ec9a4348d9ffcb19403f5b90e9eefe4cedcd6ee1", h.ComputeHash(43402934324).ToHexadecimalString()); - } - - [Fact] - public void CreateCryptoMd5_LittleEndian_ShouldBeValidHashResult() - { - var h = HashFactory.CreateCryptoMd5(o => o.ByteOrder = Endianness.LittleEndian); - Assert.Equal("193112cee1d1a35660f02e95a28bfea2", h.ComputeHash(32131535).ToHexadecimalString()); - Assert.Equal("d174ab98d277d9f5a5611c2c9f419d9f", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); - Assert.Equal("d174ab98d277d9f5a5611c2c9f419d9f", h.ComputeHash(Decorator.Enclose(Alphanumeric.LettersAndNumbers).ToStream()).ToHexadecimalString()); - Assert.Equal("781e5e245d69b566979b86e28d23f2c7", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("612f309087565745ca61c53fcaf6fa7d", h.ComputeHash(212).ToHexadecimalString()); - } - - [Fact] - public void CreateCryptoMd5_BigEndian_ShouldBeValidHashResult() - { - var h = HashFactory.CreateCryptoMd5(o => o.ByteOrder = Endianness.BigEndian); - Assert.Equal("d52654efa276b2ab12ca067dccaf953f", h.ComputeHash(32131535).ToHexadecimalString()); - Assert.Equal("d174ab98d277d9f5a5611c2c9f419d9f", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); - Assert.Equal("d174ab98d277d9f5a5611c2c9f419d9f", h.ComputeHash(Decorator.Enclose(Alphanumeric.LettersAndNumbers).ToStream()).ToHexadecimalString()); - Assert.Equal("781e5e245d69b566979b86e28d23f2c7", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("fb7b8d4f62e2be708ceca1114b439d5d", h.ComputeHash(212).ToHexadecimalString()); - } - [Fact] public void CreateFnv32_Fnv1_ShouldBeValidHashResult() { diff --git a/test/Cuemon.Core.Tests/Security/Cryptography/AesCryptorTest.cs b/test/Cuemon.Security.Cryptography.Tests/AesCryptorTest.cs similarity index 100% rename from test/Cuemon.Core.Tests/Security/Cryptography/AesCryptorTest.cs rename to test/Cuemon.Security.Cryptography.Tests/AesCryptorTest.cs diff --git a/test/Cuemon.Security.Cryptography.Tests/Cuemon.Security.Cryptography.Tests.csproj b/test/Cuemon.Security.Cryptography.Tests/Cuemon.Security.Cryptography.Tests.csproj new file mode 100644 index 000000000..8f6f64287 --- /dev/null +++ b/test/Cuemon.Security.Cryptography.Tests/Cuemon.Security.Cryptography.Tests.csproj @@ -0,0 +1,11 @@ + + + + Cuemon.Security.Cryptography + + + + + + + \ No newline at end of file diff --git a/test/Cuemon.Security.Cryptography.Tests/KeyedHashFactoryTest.cs b/test/Cuemon.Security.Cryptography.Tests/KeyedHashFactoryTest.cs new file mode 100644 index 000000000..f77524dd0 --- /dev/null +++ b/test/Cuemon.Security.Cryptography.Tests/KeyedHashFactoryTest.cs @@ -0,0 +1,58 @@ +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Security.Cryptography +{ + public class KeyedHashFactoryTest : Test + { + public KeyedHashFactoryTest(ITestOutputHelper output = null) : base(output) + { + } + + [Fact] + public void CreateHmacCryptoMd5_ShouldBeValidHashResult() + { + var h = KeyedHashFactory.CreateHmacCryptoMd5(Decorator.Enclose("unittest").ToByteArray()); + Assert.Equal("d0ee1decd115feac4608976f28e19e10", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); + Assert.Equal("34bdd10e5c71eb6860294d19f2db8233", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); + Assert.Equal("70adffcc026ac757db66de1bbb005e04", h.ComputeHash("what-a-feeling-#-¤-%-!-cover-from-dj-bobo-128379539285784289529893278278173981247983251311").ToHexadecimalString()); + } + + [Fact] + public void CreateHmacCryptoSha1_ShouldBeValidHashResult() + { + var h = KeyedHashFactory.CreateHmacCryptoSha1(Decorator.Enclose("unittest").ToByteArray()); + Assert.Equal("0c7d9d4461c66d2d6eafef7211689abd20b28b39", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); + Assert.Equal("62ea258e7e4d0522fe5e2b06e1ab600051c542c4", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); + Assert.Equal("bae9d2e09c9b5f65f9a7a3c5a55748b180ee65c1", h.ComputeHash("what-a-feeling-#-¤-%-!-cover-from-dj-bobo-128379539285784289529893278278173981247983251311").ToHexadecimalString()); + } + + [Fact] + public void CreateHmacCryptoSha256_ShouldBeValidHashResult() + { + var h = KeyedHashFactory.CreateHmacCryptoSha256(Decorator.Enclose("unittest").ToByteArray()); + Assert.Equal("c06272f221bcbfc6783c059e85d7ecdb412e80f2c2b704c27885d64d984d95e3", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); + Assert.Equal("1fd4ff25f71fa49435bc6996bbbaeeb9abe2f47ed3256b8a9f44b8eea49a23b5", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); + Assert.Equal("38367544d02183414967e4669cd298ca6d644db6c6bfa903afae497c6ab300fa", h.ComputeHash("what-a-feeling-#-¤-%-!-cover-from-dj-bobo-128379539285784289529893278278173981247983251311").ToHexadecimalString()); + } + + [Fact] + public void CreateHmacCryptoSha384_ShouldBeValidHashResult() + { + var h = KeyedHashFactory.CreateHmacCryptoSha384(Decorator.Enclose("unittest").ToByteArray()); + Assert.Equal("87d90ce62441fd39f81d327fb4d3a9902d80a0d8312a961fdfc9b1bcf756afd77f0f0596d30a868a3cda1c49abf1a3ee", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); + Assert.Equal("7a90151a628de76431d11cf01200a7213f0dc491f7677aafbcd232e512521ca1ac3771d2b6204fa1ab6b54cf085ba2d3", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); + Assert.Equal("3b00f1f207819422761a22b918c69a3e16ae0d5268c01ad331311184b6a809b927b324f75d8cb8f1cee0f849382558bd", h.ComputeHash("what-a-feeling-#-¤-%-!-cover-from-dj-bobo-128379539285784289529893278278173981247983251311").ToHexadecimalString()); + } + + [Fact] + public void CreateHmacCryptoSha512_ShouldBeValidHashResult() + { + var h = KeyedHashFactory.CreateHmacCryptoSha512(Decorator.Enclose("unittest").ToByteArray()); + Assert.Equal("db0087dde1ad907c11037243bf700a9f08430899edfc19fe1fadb7d4e6846182fa0100762cf42c64828f1bfe41493275b98f5c0c1a80300656e0d97f9f1d892e", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); + Assert.Equal("7b674e1138c2ee91828fcf5318d356e72a9c9e4e533234181640b0d1728f305db656bb7e57c95e101c0efbed9290454ef0c37f514c8dfb85f003c483bf0f236a", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); + Assert.Equal("33520bc89652184cf5a17600a78a513db44a0b7eb1fb001525ab0e77c94afcb45b7878b259cd37896ddbbc5dff76d2cbb8f57eaf44c4f4aac77695fe8fe28992", h.ComputeHash("what-a-feeling-#-¤-%-!-cover-from-dj-bobo-128379539285784289529893278278173981247983251311").ToHexadecimalString()); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Security.Cryptography.Tests/UnkeyedHashFactoryTest.cs b/test/Cuemon.Security.Cryptography.Tests/UnkeyedHashFactoryTest.cs new file mode 100644 index 000000000..dd318c37a --- /dev/null +++ b/test/Cuemon.Security.Cryptography.Tests/UnkeyedHashFactoryTest.cs @@ -0,0 +1,76 @@ +using System; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Security.Cryptography +{ + public class UnkeyedHashFactoryTest : Test + { + public UnkeyedHashFactoryTest(ITestOutputHelper output = null) : base(output) + { + } + + [Fact] + public void CreateCryptoSha512_ShouldBeValidHashResult() + { + var h = UnkeyedHashFactory.CreateCryptoSha512(); + Assert.Equal("1e07be23c26a86ea37ea810c8ec7809352515a970e9253c26f536cfc7a9996c45c8370583e0a78fa4a90041d71a4ceab7423f19c71b9d5a3e01249f0bebd5894", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); + Assert.Equal("bb96c2fc40d2d54617d6f276febe571f623a8dadf0b734855299b0e107fda32cf6b69f2da32b36445d73690b93cbd0f7bfc20e0f7f28553d2a4428f23b716e90", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); + Assert.Equal("0b6cbac838dfe7f47ea1bd0df00ec282fdf45510c92161072ccfb84035390c4da743d9c3b954eaa1b0f86fc9861b23cc6c8667ab232c11c686432ebb5c8c3f27", h.ComputeHash(Guid.Empty.ToByteArray()).ToHexadecimalString()); + } + + [Fact] + public void CreateCryptoSha384_ShouldBeValidHashResult() + { + var h = UnkeyedHashFactory.CreateCryptoSha384(); + Assert.Equal("1761336e3f7cbfe51deb137f026f89e01a448e3b1fafa64039c1464ee8732f11a5341a6f41e0c202294736ed64db1a84", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); + Assert.Equal("90ae531f24e48697904a4d0286f354c50a350ebb6c2b9efcb22f71c96ceaeffc11c6095e9ca0df0ec30bf685dcf2e5e5", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); + Assert.Equal("7210af19145ec2a8e250a7fe8e9eeeac1301e524daab82366c36be614dc35402a289101e48cad61c45337f2f32c14fdc", h.ComputeHash(VerticalDirection.Up).ToHexadecimalString()); + } + + [Fact] + public void CreateCryptoSha256_ShouldBeValidHashResult() + { + var h = UnkeyedHashFactory.CreateCryptoSha256(); + Assert.Equal("db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); + Assert.Equal("db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0", h.ComputeHash(Decorator.Enclose(Alphanumeric.LettersAndNumbers).ToStream()).ToHexadecimalString()); + Assert.Equal("84d89877f0d4041efb6bf91a16f0248f2fd573e6af05c19f96bedb9f882f7882", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); + Assert.Equal("53ab3a50f51855beeae9721ab68656312c7f105b9b34bbfa97875dbfda72dbc6", h.ComputeHash(DateTime.UnixEpoch).ToHexadecimalString()); + Assert.Equal("1f1a24c833be74a0f4f99007aa70a51e2456e41f745a5628721ea2b8e1c07641", h.ComputeHash(213, "fdfsfsf", 9999).ToHexadecimalString()); + } + + [Fact] + public void CreateCryptoSha1_ShouldBeValidHashResult() + { + var h = UnkeyedHashFactory.CreateCryptoSha1(); + Assert.Equal("761c457bf73b14d27e9e9265c46f4b4dda11f940", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); + Assert.Equal("761c457bf73b14d27e9e9265c46f4b4dda11f940", h.ComputeHash(Decorator.Enclose(Alphanumeric.LettersAndNumbers).ToStream()).ToHexadecimalString()); + Assert.Equal("87acec17cd9dcd20a716cc2cf67417b71c8a7016", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); + Assert.Equal("485bad9954c874513b8ff7b6d5ea459ac65dd075", h.ComputeHash(decimal.MinValue).ToHexadecimalString()); + Assert.Equal("ec9a4348d9ffcb19403f5b90e9eefe4cedcd6ee1", h.ComputeHash(43402934324).ToHexadecimalString()); + } + + [Fact] + public void CreateCryptoMd5_LittleEndian_ShouldBeValidHashResult() + { + var h = UnkeyedHashFactory.CreateCryptoMd5(o => o.ByteOrder = Endianness.LittleEndian); + Assert.Equal("193112cee1d1a35660f02e95a28bfea2", h.ComputeHash(32131535).ToHexadecimalString()); + Assert.Equal("d174ab98d277d9f5a5611c2c9f419d9f", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); + Assert.Equal("d174ab98d277d9f5a5611c2c9f419d9f", h.ComputeHash(Decorator.Enclose(Alphanumeric.LettersAndNumbers).ToStream()).ToHexadecimalString()); + Assert.Equal("781e5e245d69b566979b86e28d23f2c7", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); + Assert.Equal("612f309087565745ca61c53fcaf6fa7d", h.ComputeHash(212).ToHexadecimalString()); + } + + [Fact] + public void CreateCryptoMd5_BigEndian_ShouldBeValidHashResult() + { + var h = UnkeyedHashFactory.CreateCryptoMd5(o => o.ByteOrder = Endianness.BigEndian); + Assert.Equal("d52654efa276b2ab12ca067dccaf953f", h.ComputeHash(32131535).ToHexadecimalString()); + Assert.Equal("d174ab98d277d9f5a5611c2c9f419d9f", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); + Assert.Equal("d174ab98d277d9f5a5611c2c9f419d9f", h.ComputeHash(Decorator.Enclose(Alphanumeric.LettersAndNumbers).ToStream()).ToHexadecimalString()); + Assert.Equal("781e5e245d69b566979b86e28d23f2c7", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); + Assert.Equal("fb7b8d4f62e2be708ceca1114b439d5d", h.ComputeHash(212).ToHexadecimalString()); + } + } +} \ No newline at end of file From 1eb603d2edc2c44718dcca17a6d6838df9c667d2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 17:04:49 +0200 Subject: [PATCH 104/385] Changed from crypto hash to non-crypto hash. --- src/Cuemon.Net/NetWatcher.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cuemon.Net/NetWatcher.cs b/src/Cuemon.Net/NetWatcher.cs index cf1fe4e6d..9bb581f7b 100644 --- a/src/Cuemon.Net/NetWatcher.cs +++ b/src/Cuemon.Net/NetWatcher.cs @@ -3,7 +3,7 @@ using System.IO; using Cuemon.Net.Http; using Cuemon.Runtime; -using Cuemon.Security.Cryptography; +using Cuemon.Security; using Cuemon.Text; namespace Cuemon.Net @@ -163,7 +163,7 @@ private void HandleSignalingFile(ref DateTime utcLastModified, ref string curren using (var stream = new FileStream(RequestUri.LocalPath, FileMode.Open, FileAccess.Read)) { stream.Position = 0; - currentSignature = HashFactory.CreateCryptoSha256().ComputeHash(stream).ToHexadecimalString(); + currentSignature = HashFactory.CreateFnv256().ComputeHash(stream).ToHexadecimalString(); } } } @@ -180,7 +180,7 @@ private void HandleSignalingHttp(ref DateTime utcLastModified, ref string curren { case HttpMethods.Get: var etag = response.Headers.ETag; - currentSignature = string.IsNullOrEmpty(etag.Tag) ? HashFactory.CreateCryptoSha256().ComputeHash(response.Content.ReadAsByteArrayAsync().Result).ToHexadecimalString() : etag.Tag; + currentSignature = string.IsNullOrEmpty(etag.Tag) ? HashFactory.CreateFnv256().ComputeHash(response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult()).ToHexadecimalString() : etag.Tag; break; case HttpMethods.Head: utcLastModified = response.Content.Headers.LastModified?.UtcDateTime ?? DateTime.MaxValue; From 351b7b661c14213f9da062926d68b138c950ba7c Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 17:05:00 +0200 Subject: [PATCH 105/385] Removed a using. --- src/Cuemon.Core/Template.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Cuemon.Core/Template.cs b/src/Cuemon.Core/Template.cs index cb3eff0c7..b47177f0e 100644 --- a/src/Cuemon.Core/Template.cs +++ b/src/Cuemon.Core/Template.cs @@ -1,5 +1,4 @@ -using System; -using Cuemon.Collections.Generic; +using Cuemon.Collections.Generic; namespace Cuemon { From 3fd20c1630b749ad1b8e61077f4875abafd31ff5 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 18:23:37 +0200 Subject: [PATCH 106/385] Consequence changes after refactorings of Cuemon.Security.Cryptography and Cuemon.Data.Integrity. --- .../CacheableObjectFactory.cs | 4 ++-- .../ContentBasedObjectResult.cs | 10 ++++---- .../ContentTimeBasedObjectResult.cs | 6 ++--- .../Cuemon.AspNetCore.Mvc.csproj | 2 +- .../Cacheable/HttpEntityTagHeaderFilter.cs | 4 ++-- .../Cacheable/HttpEntityTagHeaderOptions.cs | 24 ++++++++++--------- .../Cacheable/HttpLastModifiedHeaderFilter.cs | 4 ++-- .../HttpLastModifiedHeaderOptions.cs | 2 +- .../ICacheableObjectResult.cs | 2 +- 9 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/Cuemon.AspNetCore.Mvc/CacheableObjectFactory.cs b/src/Cuemon.AspNetCore.Mvc/CacheableObjectFactory.cs index f7ac1db7e..e60a5f1e1 100644 --- a/src/Cuemon.AspNetCore.Mvc/CacheableObjectFactory.cs +++ b/src/Cuemon.AspNetCore.Mvc/CacheableObjectFactory.cs @@ -86,7 +86,7 @@ public static ICacheableObjectResult CreateCacheableObjectResult(T instance, /// An implementation. /// /// - /// + /// /// /// /// @@ -110,7 +110,7 @@ public static ICacheableObjectResult CreateCacheableObjectResult(object instance /// An implementation. /// /// - /// + /// /// /// /// diff --git a/src/Cuemon.AspNetCore.Mvc/ContentBasedObjectResult.cs b/src/Cuemon.AspNetCore.Mvc/ContentBasedObjectResult.cs index 40168ad01..39ae30f86 100644 --- a/src/Cuemon.AspNetCore.Mvc/ContentBasedObjectResult.cs +++ b/src/Cuemon.AspNetCore.Mvc/ContentBasedObjectResult.cs @@ -1,5 +1,5 @@ using Cuemon.Data.Integrity; -using Cuemon.Security.Cryptography; +using Cuemon.Security; namespace Cuemon.AspNetCore.Mvc { @@ -9,13 +9,13 @@ internal ContentBasedObjectResult(object instance, byte[] checksum, bool isWeak { Checksum = new HashResult(checksum); Validation = checksum == null || checksum.Length == 0 - ? EntityDataIntegrityStrength.Unspecified + ? EntityDataIntegrityValidation.Unspecified : isWeak - ? EntityDataIntegrityStrength.Weak - : EntityDataIntegrityStrength.Strong; + ? EntityDataIntegrityValidation.Weak + : EntityDataIntegrityValidation.Strong; } - public EntityDataIntegrityStrength Validation { get; } + public EntityDataIntegrityValidation Validation { get; } public HashResult Checksum { get; } } diff --git a/src/Cuemon.AspNetCore.Mvc/ContentTimeBasedObjectResult.cs b/src/Cuemon.AspNetCore.Mvc/ContentTimeBasedObjectResult.cs index 6a0d56def..878ebecaa 100644 --- a/src/Cuemon.AspNetCore.Mvc/ContentTimeBasedObjectResult.cs +++ b/src/Cuemon.AspNetCore.Mvc/ContentTimeBasedObjectResult.cs @@ -1,10 +1,10 @@ using System; using Cuemon.Data.Integrity; -using Cuemon.Security.Cryptography; +using Cuemon.Security; namespace Cuemon.AspNetCore.Mvc { - internal class ContentTimeBasedObjectResult : CacheableObjectResult, IEntityData + internal class ContentTimeBasedObjectResult : CacheableObjectResult, IEntityInfo { internal ContentTimeBasedObjectResult(object instance, IEntityDataTimestamp timestamp, IEntityDataIntegrity dataIntegrity) : base(instance) { @@ -18,7 +18,7 @@ internal ContentTimeBasedObjectResult(object instance, IEntityDataTimestamp time public DateTime? Modified { get; set; } - public EntityDataIntegrityStrength Validation { get; set; } + public EntityDataIntegrityValidation Validation { get; set; } public HashResult Checksum { get; set; } } diff --git a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj index 0764b0e9d..2a90364a6 100644 --- a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj +++ b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj @@ -18,7 +18,7 @@ - + \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderFilter.cs index 9dae8427b..466492263 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderFilter.cs @@ -19,8 +19,8 @@ public class HttpEntityTagHeaderFilter : IConfigurable /// Initializes a new instance of the class. /// - /// The which need to be configured. - public HttpEntityTagHeaderFilter(Action setup) + /// The which may be configured. + public HttpEntityTagHeaderFilter(Action setup = null) { Options = Patterns.Configure(setup); } diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs index 135d41d50..647f73790 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs @@ -2,6 +2,8 @@ using System.IO; using Cuemon.AspNetCore.Http; using Cuemon.Data.Integrity; +using Cuemon.IO; +using Cuemon.Security; using Cuemon.Security.Cryptography; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -30,8 +32,8 @@ public class HttpEntityTagHeaderOptions /// /// (integrity, context) => /// { - /// var builder = new ChecksumBuilder(integrity.Checksum.Value); - /// context.Response.SetEntityTagHeaderInformation(context.Request, builder, integrity.ChecksumStrength == ChecksumStrength.Weak); + /// var builder = new ChecksumBuilder(integrity.Checksum.GetBytes(), () => HashFactory.CreateFnv128()); + /// Decorator.Enclose(context.Response).TryAddOrUpdateEntityTagHeader(context.Request, builder, integrity.Validation == EntityDataIntegrityValidation.Weak); /// }; /// /// @@ -40,12 +42,10 @@ public class HttpEntityTagHeaderOptions /// /// (body, request, response) => /// { - /// var builder = new ChecksumBuilder(body.ComputeHash(o => - /// { - /// o.AlgorithmType = HashAlgorithmType.MD5; - /// o.LeaveStreamOpen = true; - /// }).Value); - /// response.SetEntityTagHeaderInformation(request, builder); + /// var ms = new MemoryStream(); + /// Decorator.Enclose(body).CopyStream(ms); + /// var builder = new ChecksumBuilder(ms.ToArray(), () => UnkeyedHashFactory.CreateCryptoMd5()); + /// Decorator.Enclose(response).TryAddOrUpdateEntityTagHeader(request, builder); /// }; /// /// @@ -59,12 +59,14 @@ public HttpEntityTagHeaderOptions() { EntityTagProvider = (integrity, context) => { - var builder = new ChecksumBuilder(integrity.Checksum.GetBytes()); - Decorator.Enclose(context.Response).TryAddOrUpdateEntityTagHeader(context.Request, builder, integrity.Validation == EntityDataIntegrityStrength.Weak); + var builder = new ChecksumBuilder(integrity.Checksum.GetBytes(), () => HashFactory.CreateFnv128()); + Decorator.Enclose(context.Response).TryAddOrUpdateEntityTagHeader(context.Request, builder, integrity.Validation == EntityDataIntegrityValidation.Weak); }; EntityTagResponseParser = (body, request, response) => { - var builder = new ChecksumBuilder(HashFactory.CreateCrypto(CryptoAlgorithm.Md5).ComputeHash(body).GetBytes()); + var ms = new MemoryStream(); + Decorator.Enclose(body).CopyStream(ms); + var builder = new ChecksumBuilder(ms.ToArray(), () => UnkeyedHashFactory.CreateCryptoMd5()); Decorator.Enclose(response).TryAddOrUpdateEntityTagHeader(request, builder); }; UseEntityTagResponseParser = false; diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderFilter.cs index 3220fd154..11b7dc967 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderFilter.cs @@ -17,8 +17,8 @@ public class HttpLastModifiedHeaderFilter : IConfigurable /// Initializes a new instance of the class. /// - /// The which need to be configured. - public HttpLastModifiedHeaderFilter(Action setup) + /// The which may be configured. + public HttpLastModifiedHeaderFilter(Action setup = null) { Options = Patterns.Configure(setup); } diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs index c4f2ad8f3..f33249bd7 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs @@ -28,7 +28,7 @@ public class HttpLastModifiedHeaderOptions /// /// (timestamp, context) => /// { - /// context.Response.SetLastModifiedHeaderInformation(context.Request, timestamp.Modified ?? timestamp.Created); + /// Decorator.Enclose(context.Response).TryAddOrUpdateLastModifiedHeader(context.Request, timestamp.Modified ?? timestamp.Created); /// }; /// /// diff --git a/src/Cuemon.AspNetCore.Mvc/ICacheableObjectResult.cs b/src/Cuemon.AspNetCore.Mvc/ICacheableObjectResult.cs index 68b12a07b..ae9ed9c80 100644 --- a/src/Cuemon.AspNetCore.Mvc/ICacheableObjectResult.cs +++ b/src/Cuemon.AspNetCore.Mvc/ICacheableObjectResult.cs @@ -7,7 +7,7 @@ namespace Cuemon.AspNetCore.Mvc /// /// . /// . - /// . + /// . public interface ICacheableObjectResult { /// From 0b0bcb3c52ab4ad181c7adc4c5b2d09cba4a4489 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 18:29:43 +0200 Subject: [PATCH 107/385] Added properties with assembly directives (consistency). --- .../Properties/AssemblyInfo.cs | 4 ++++ src/Cuemon.AspNetCore.Razor/Properties/AssemblyInfo.cs | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 src/Cuemon.AspNetCore.Authentication/Properties/AssemblyInfo.cs create mode 100644 src/Cuemon.AspNetCore.Razor/Properties/AssemblyInfo.cs diff --git a/src/Cuemon.AspNetCore.Authentication/Properties/AssemblyInfo.cs b/src/Cuemon.AspNetCore.Authentication/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..20b98e714 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("cbba5566-2383-459e-86d5-8a1fc232e8ed")] \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Razor/Properties/AssemblyInfo.cs b/src/Cuemon.AspNetCore.Razor/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..8db3562bc --- /dev/null +++ b/src/Cuemon.AspNetCore.Razor/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("74d499ee-862e-412b-9a2d-fb2fe30d518e")] \ No newline at end of file From 20a78f42040510cde4dd67697b63ea4143ae5a97 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 18:32:15 +0200 Subject: [PATCH 108/385] Unicode. --- .../Cuemon.AspNetCore.Authentication.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj index f814a7cc8..6f72201cf 100644 --- a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj +++ b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.0;netstandard2.0 From e73fecbe6910cf58507fd9ed8122b05f1606e273 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 18:41:00 +0200 Subject: [PATCH 109/385] Adjusted code back from NET Core 1 (should probably remove line 28-47). --- .../Globalization/RegionInfoExtensions.cs | 23 ------------------- src/Cuemon.Core/Globalization/World.cs | 15 ++++++++---- 2 files changed, 11 insertions(+), 27 deletions(-) delete mode 100644 src/Cuemon.Core/Globalization/RegionInfoExtensions.cs diff --git a/src/Cuemon.Core/Globalization/RegionInfoExtensions.cs b/src/Cuemon.Core/Globalization/RegionInfoExtensions.cs deleted file mode 100644 index 2384ba98e..000000000 --- a/src/Cuemon.Core/Globalization/RegionInfoExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -namespace Cuemon.Globalization -{ - /// - /// This is an extension implementation of the class. - /// - public static class RegionInfoExtensions - { - /// - /// Resolves a sequence of related objects for the specified . - /// - /// The region to resolve a sequence of objects from. - /// An sequence of objects. - public static IEnumerable GetCultures(this RegionInfo region) - { - Validator.ThrowIfNull(region, nameof(region)); - return World.SpecificCultures.Value.Where(c => c.Name.EndsWith(region.TwoLetterISORegionName)); - } - } -} \ No newline at end of file diff --git a/src/Cuemon.Core/Globalization/World.cs b/src/Cuemon.Core/Globalization/World.cs index d4db3ec3b..c63fddafa 100644 --- a/src/Cuemon.Core/Globalization/World.cs +++ b/src/Cuemon.Core/Globalization/World.cs @@ -3,7 +3,6 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Reflection; using Cuemon.Reflection; namespace Cuemon.Globalization @@ -13,9 +12,6 @@ namespace Cuemon.Globalization /// public static class World { - private const int CultureTypesSpecificCultures = 2; - private static readonly MethodInfo CultureInfoGetCultures = typeof(CultureInfo).GetMethod("GetCultures", new MemberReflection(excludeInheritancePath: true)); - internal static readonly Lazy> SpecificCultures = new Lazy>(() => { var cultures = new SortedList(); @@ -67,5 +63,16 @@ public static class World /// /// The .NET specific regions of the world. public static IEnumerable Regions { get; } = SpecificRegions.Value; + + /// + /// Resolves a sequence of related objects for the specified . + /// + /// The region to resolve a sequence of objects from. + /// An sequence of objects. + public static IEnumerable GetCultures(RegionInfo region) + { + Validator.ThrowIfNull(region, nameof(region)); + return SpecificCultures.Value.Where(c => c.Name.EndsWith(region.TwoLetterISORegionName)); + } } } \ No newline at end of file From 9877c329ae4e0e8f55ae61dd52e941cf1e80a361 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 20:40:02 +0200 Subject: [PATCH 110/385] Encoding - don't know why it's happening. --- src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj b/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj index 63d4d46da..14e333e79 100644 --- a/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj +++ b/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.0;netstandard2.0 From e1a9b42f59582e20022276bc4f40046396bad5fe Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 20:40:26 +0200 Subject: [PATCH 111/385] Fixed issues reported by SonarCloud. --- src/Cuemon.Core/GlobalSuppressions.cs | 72 +++++++++++++++++-- .../GlobalSuppressions.cs | 8 +++ src/Cuemon.Data/GlobalSuppressions.cs | 6 ++ .../AssemblyCacheBustingOptions.cs | 2 +- 4 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 src/Cuemon.Data.Integrity/GlobalSuppressions.cs diff --git a/src/Cuemon.Core/GlobalSuppressions.cs b/src/Cuemon.Core/GlobalSuppressions.cs index f575988b4..801679851 100644 --- a/src/Cuemon.Core/GlobalSuppressions.cs +++ b/src/Cuemon.Core/GlobalSuppressions.cs @@ -47,10 +47,68 @@ [assembly: SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Validator.ThrowIfWhiteSpace(System.String,System.String,System.String)")] [assembly: SuppressMessage("Major Code Smell", "S3881:\"IDisposable\" should be implemented correctly", Justification = "This is a base class implementation of the IDisposable interface tailored to avoid wrong implementations.", Scope = "type", Target = "~T:Cuemon.Disposable")] [assembly: SuppressMessage("Minor Code Smell", "S1128:Unused \"using\" should be removed", Justification = "It is actually used when resolving extension method from System.Collections.Generic; SC just can't figure this out.")] -[assembly: SuppressMessage("Major Code Smell", "S1168:Empty arrays and collections should be returned instead of null", Justification = "By design; property serves it purpose.", Scope = "member", Target = "~P:Cuemon.Data.ConcurrentDsvDataReader.NullRead")] -[assembly: SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "By design to help clarify context.", Scope = "member", Target = "~M:Cuemon.Data.ConcurrentDsvDataReader.ReadNext(System.String[])~System.String[]")] -[assembly: SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "By design to help clarify context.", Scope = "member", Target = "~M:Cuemon.Data.DsvDataReader.ReadNext(System.String[])~System.String[]")] -[assembly: SuppressMessage("Major Code Smell", "S1168:Empty arrays and collections should be returned instead of null", Justification = "By design; property serves it purpose.", Scope = "member", Target = "~P:Cuemon.Data.DsvDataReader.NullRead")] -[assembly: SuppressMessage("Minor Code Smell", "S1199:Nested code blocks should not be used", Justification = "By design.", Scope = "member", Target = "~M:Cuemon.IO.StreamFactory.CreateStreamCore``1(Cuemon.ActionFactory{``0},System.Action{Cuemon.IO.StreamWriterOptions})~System.IO.Stream")] -[assembly: SuppressMessage("Major Bug", "S1751:Loops with at most one iteration should be refactored", Justification = "This is by design and how a reader is implemented. While there are lines to be read, we built a token, and the token is being read. When read, it returns true and proceeds to next line.", Scope = "member", Target = "~M:Cuemon.Data.DsvDataReader.Read~System.Boolean")] -[assembly: SuppressMessage("Major Bug", "S1751:Loops with at most one iteration should be refactored", Justification = "This is by design and how a reader is implemented. While there are lines to be read, we built a token, and the token is being read. When read, it returns true and proceeds to next line.", Scope = "member", Target = "~M:Cuemon.Data.ConcurrentDsvDataReader.ReadAsync~System.Threading.Tasks.Task{System.Boolean}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateEighteen``18(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateEighteen``18(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "", Scope = "member", Target = "~M:Cuemon.Template.CreateEleven``11(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateFifteen``15(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateFifteen``15(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateFourteen``14(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateFourteen``14(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateNineteen``19(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17,``18)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17,``18}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateNineteen``19(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17,``18)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17,``18}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateSeventeen``17(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateSeventeen``17(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateSixteen``16(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateSixteen``16(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateThirteen``13(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateThirteen``13(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateTwelve``12(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateTwelve``12(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateTwenty``20(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17,``18,``19)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17,``18,``19}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateTwenty``20(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17,``18,``19)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17,``18,``19}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template`8.#ctor(`0,`1,`2,`3,`4,`5,`6,`7)")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`10")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`10")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`11")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`11")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`12")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`12")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`13")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`13")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`14")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`14")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`15")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`15")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`16")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`16")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`17")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`17")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`18")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`18")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`19")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`19")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`20")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`20")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`4")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`5")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`6")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`6")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`7")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`7")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`8")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`8")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "type", Target = "~T:Cuemon.Template`9")] +[assembly: SuppressMessage("Major Code Smell", "S110:Inheritance tree of classes should not be too deep", Justification = "By design; provides invaluable information about arguments given to any factory implementation.", Scope = "type", Target = "~T:Cuemon.Template`9")] +[assembly: SuppressMessage("Critical Code Smell", "S3776:Cognitive Complexity of methods should not be too high", Justification = "By design.", Scope = "member", Target = "~F:Cuemon.Convertible.EndianSensitiveByteArrayConverters")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateEight``8(``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateEight``8(``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateEleven``11(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateFive``5(``0,``1,``2,``3,``4)~Cuemon.Template{``0,``1,``2,``3,``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateFour``4(``0,``1,``2,``3)~Cuemon.Template{``0,``1,``2,``3}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateNine``9(``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateNine``9(``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateSeven``7(``0,``1,``2,``3,``4,``5,``6)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateSix``6(``0,``1,``2,``3,``4,``5)~Cuemon.Template{``0,``1,``2,``3,``4,``5}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateTen``10(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateTen``10(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9}")] diff --git a/src/Cuemon.Data.Integrity/GlobalSuppressions.cs b/src/Cuemon.Data.Integrity/GlobalSuppressions.cs new file mode 100644 index 000000000..f62ceb404 --- /dev/null +++ b/src/Cuemon.Data.Integrity/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Major Bug", "S2259:Null pointers should not be dereferenced", Justification = "False-Positive", Scope = "member", Target = "~M:Cuemon.Data.Integrity.CacheValidator.#ctor(Cuemon.Data.Integrity.EntityInfo,System.Func{Cuemon.Security.Hash},Cuemon.Data.Integrity.EntityDataIntegrityMethod)")] diff --git a/src/Cuemon.Data/GlobalSuppressions.cs b/src/Cuemon.Data/GlobalSuppressions.cs index 1767d078c..d5b468c7b 100644 --- a/src/Cuemon.Data/GlobalSuppressions.cs +++ b/src/Cuemon.Data/GlobalSuppressions.cs @@ -6,3 +6,9 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Major Bug", "S2259:Null pointers should not be dereferenced", Justification = "The variable, fieldCount, is initialized to -1 in case of record being null - hence, the for loop will never iterate.", Scope = "member", Target = "~M:Cuemon.Data.DataTransferColumnCollection.#ctor(System.Data.IDataRecord)")] +[assembly: SuppressMessage("Major Bug", "S1751:Loops with at most one iteration should be refactored", Justification = "This is by design and how a reader is implemented. While there are lines to be read, we built a token, and the token is being read. When read, it returns true and proceeds to next line.", Scope = "member", Target = "~M:Cuemon.Data.DsvDataReader.Read~System.Boolean")] +[assembly: SuppressMessage("Major Bug", "S1751:Loops with at most one iteration should be refactored", Justification = "This is by design and how a reader is implemented. While there are lines to be read, we built a token, and the token is being read. When read, it returns true and proceeds to next line.", Scope = "member", Target = "~M:Cuemon.Data.ConcurrentDsvDataReader.ReadAsync~System.Threading.Tasks.Task{System.Boolean}")] +[assembly: SuppressMessage("Major Code Smell", "S1168:Empty arrays and collections should be returned instead of null", Justification = "By design; property serves it purpose.", Scope = "member", Target = "~P:Cuemon.Data.ConcurrentDsvDataReader.NullRead")] +[assembly: SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "By design to help clarify context.", Scope = "member", Target = "~M:Cuemon.Data.ConcurrentDsvDataReader.ReadNext(System.String[])~System.String[]")] +[assembly: SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "By design to help clarify context.", Scope = "member", Target = "~M:Cuemon.Data.DsvDataReader.ReadNext(System.String[])~System.String[]")] +[assembly: SuppressMessage("Major Code Smell", "S1168:Empty arrays and collections should be returned instead of null", Justification = "By design; property serves it purpose.", Scope = "member", Target = "~P:Cuemon.Data.DsvDataReader.NullRead")] \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs index c598e648f..60ac4d9ee 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs @@ -25,7 +25,7 @@ public class AssemblyCacheBustingOptions : CacheBustingOptions /// /// /// - /// + /// /// /// /// From dc14f03bf544420d68aaf0c617a6e9b0d9f0f76b Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 21:03:24 +0200 Subject: [PATCH 112/385] SonarCloud justifications. --- src/Cuemon.AspNetCore.Mvc/GlobalSuppressions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Cuemon.AspNetCore.Mvc/GlobalSuppressions.cs b/src/Cuemon.AspNetCore.Mvc/GlobalSuppressions.cs index 6b988a9ce..9b5d87846 100644 --- a/src/Cuemon.AspNetCore.Mvc/GlobalSuppressions.cs +++ b/src/Cuemon.AspNetCore.Mvc/GlobalSuppressions.cs @@ -6,3 +6,6 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Major Code Smell", "S3358:Ternary operators should not be nested", Justification = "Clear enough.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Mvc.ContentBasedObjectResult.#ctor(System.Object,System.Byte[],System.Boolean)")] +[assembly: SuppressMessage("Major Code Smell", "S1066:Collapsible \"if\" statements should be merged", Justification = "By design; easier for debug purposes and with clear scope.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Mvc.Filters.Cacheable.HttpLastModifiedHeaderFilter.OnResultExecutionAsync(Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext,Microsoft.AspNetCore.Mvc.Filters.ResultExecutionDelegate)~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S1066:Collapsible \"if\" statements should be merged", Justification = "By design; easier for debug purposes and with clear scope.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Mvc.Filters.Cacheable.HttpEntityTagHeaderFilter.OnResultExecutionAsync(Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext,Microsoft.AspNetCore.Mvc.Filters.ResultExecutionDelegate)~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Critical Code Smell", "S3776:Cognitive Complexity of methods should not be too high", Justification = "If i invert the if-statement, the warning goes away - but the code becomes harder to read. So for now, i exclude it as 'by design'.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Mvc.Filters.Diagnostics.FaultDescriptorFilter.OnException(Microsoft.AspNetCore.Mvc.Filters.ExceptionContext)")] From 538833095307ff4216c41656e9d5fff8d771b52b Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 22:27:01 +0200 Subject: [PATCH 113/385] Added some unit tests. --- test/Cuemon.Core.Tests/BinaryPrefixTest.cs | 26 ++++ .../Cuemon.Core.Tests/BitMultipleTableTest.cs | 133 ++++++++++++++++++ test/Cuemon.Core.Tests/DecimalPrefixTest.cs | 28 ++++ 3 files changed, 187 insertions(+) create mode 100644 test/Cuemon.Core.Tests/BinaryPrefixTest.cs create mode 100644 test/Cuemon.Core.Tests/BitMultipleTableTest.cs create mode 100644 test/Cuemon.Core.Tests/DecimalPrefixTest.cs diff --git a/test/Cuemon.Core.Tests/BinaryPrefixTest.cs b/test/Cuemon.Core.Tests/BinaryPrefixTest.cs new file mode 100644 index 000000000..302d0ed72 --- /dev/null +++ b/test/Cuemon.Core.Tests/BinaryPrefixTest.cs @@ -0,0 +1,26 @@ +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon +{ + public class BinaryPrefixTest : Test + { + public BinaryPrefixTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void BinaryPrefix_ShouldVerifyMultiplePrefixConstants() + { + Assert.Equal(BinaryPrefix.Kibi.Multiplier, 1024d); + Assert.Equal(BinaryPrefix.Mebi.Multiplier, 1048576d); + Assert.Equal(BinaryPrefix.Gibi.Multiplier, 1073741824d); + Assert.Equal(BinaryPrefix.Tebi.Multiplier, 1099511627776d); + Assert.Equal(BinaryPrefix.Pebi.Multiplier, 1125899906842624d); + Assert.Equal(BinaryPrefix.Exbi.Multiplier, 1152921504606846976d); + Assert.Equal(BinaryPrefix.Zebi.Multiplier, 1180591620717411303424d); + Assert.Equal(BinaryPrefix.Yobi.Multiplier, 1208925819614629174706176d); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Core.Tests/BitMultipleTableTest.cs b/test/Cuemon.Core.Tests/BitMultipleTableTest.cs new file mode 100644 index 000000000..4c0f3f87f --- /dev/null +++ b/test/Cuemon.Core.Tests/BitMultipleTableTest.cs @@ -0,0 +1,133 @@ +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon +{ + public class BitMultipleTableTest : Test + { + public BitMultipleTableTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void BitMultipleTable_ShouldBeEqualWithAFactorOfEight() + { + var x = BitMultipleTable.FromBytes(1000000000); + var y = BitMultipleTable.FromBits(8000000000); + Assert.Equal(x, y); + } + + [Fact] + public void BitMultipleTable_UseSymbol_ShouldConvertOneBillionBitsToBinaryAndMetricPrefixToStringRepresentation() + { + var x = BitMultipleTable.FromBits(1000000000); + + Assert.Equal("0 Pib", x.Pebi.ToString()); + Assert.Equal("0 Tib", x.Tebi.ToString()); + Assert.Equal("0.93 Gib", x.Gibi.ToString()); + Assert.Equal("953.67 Mib", x.Mebi.ToString()); + Assert.Equal("976,562.5 Kib", x.Kibi.ToString()); + + Assert.Equal("0 Pb", x.Peta.ToString()); + Assert.Equal("0 Tb", x.Tera.ToString()); + Assert.Equal("1 Gb", x.Giga.ToString()); + Assert.Equal("1,000 Mb", x.Mega.ToString()); + Assert.Equal("1,000,000 kb", x.Kilo.ToString()); + + Assert.Equal("1,000,000,000 b", x.Unit.ToString()); + + TestOutput.WriteLine(x.ToAggregateString()); + } + + [Fact] + public void BitMultipleTable_UseCompound_ShouldConvertOneBillionBitsToBinaryAndMetricPrefixToStringRepresentation() + { + var x = BitMultipleTable.FromBits(1000000000, o => o.Style = NamingStyle.Compound); + + Assert.Equal("0 pebibit", x.Pebi.ToString()); + Assert.Equal("0 tebibit", x.Tebi.ToString()); + Assert.Equal("0.93 gibibit", x.Gibi.ToString()); + Assert.Equal("953.67 mebibit", x.Mebi.ToString()); + Assert.Equal("976,562.5 kibibit", x.Kibi.ToString()); + + Assert.Equal("0 petabit", x.Peta.ToString()); + Assert.Equal("0 terabit", x.Tera.ToString()); + Assert.Equal("1 gigabit", x.Giga.ToString()); + Assert.Equal("1,000 megabit", x.Mega.ToString()); + Assert.Equal("1,000,000 kilobit", x.Kilo.ToString()); + + Assert.Equal("1,000,000,000 bit", x.Unit.ToString()); + + TestOutput.WriteLine(x.ToAggregateString()); + } + + public void BitMultipleTable_UseCompound_ShouldConvertOneBillionBitsToBinaryAndMetricPrefixDoubleRepresentation() + { + var bs = BitMultipleTable.FromBits(1000000000, o => + { + o.Style = NamingStyle.Compound; + o.Prefix = UnitPrefix.Decimal; + }); + + Assert.Equal(1000000000, (double)bs); + Assert.Equal(1000000, bs.Kilo.PrefixValue); + Assert.Equal(1000, bs.Mega.PrefixValue); + Assert.Equal(1, bs.Giga.PrefixValue); + Assert.Equal(0.001, bs.Tera.PrefixValue); + Assert.Equal(1E-06, bs.Peta.PrefixValue); + Assert.Equal(976562.5, bs.Kibi.PrefixValue); + Assert.Equal(953.67431640625, bs.Mebi.PrefixValue); + Assert.Equal(0.93132257461547852, bs.Gibi.PrefixValue); + Assert.Equal(0.00090949470177292824, bs.Tebi.PrefixValue); + Assert.Equal(8.8817841970012523E-07, bs.Pebi.PrefixValue); + Assert.Equal("1 gigabit", bs.ToString()); + + TestOutput.WriteLine(bs.ToString()); + } + + [Fact] + public void BitMultipleTable_UseSymbol_ShouldConvertOneBillionBytesToBinaryAndMetricPrefixToStringRepresentation() + { + var x = ByteMultipleTable.FromBytes(1000000000); + + Assert.Equal("0 PiB", x.Pebi.ToString()); + Assert.Equal("0 TiB", x.Tebi.ToString()); + Assert.Equal("0.93 GiB", x.Gibi.ToString()); + Assert.Equal("953.67 MiB", x.Mebi.ToString()); + Assert.Equal("976,562.5 KiB", x.Kibi.ToString()); + + Assert.Equal("0 PB", x.Peta.ToString()); + Assert.Equal("0 TB", x.Tera.ToString()); + Assert.Equal("1 GB", x.Giga.ToString()); + Assert.Equal("1,000 MB", x.Mega.ToString()); + Assert.Equal("1,000,000 kB", x.Kilo.ToString()); + + Assert.Equal("1,000,000,000 B", x.Unit.ToString()); + + TestOutput.WriteLine(x.ToAggregateString()); + } + + [Fact] + public void BitMultipleTable_UseCompound_ShouldConvertOneBillionBytesToBinaryAndMetricPrefixToStringRepresentation() + { + var x = ByteMultipleTable.FromBytes(1000000000, o => o.Style = NamingStyle.Compound); + + Assert.Equal("0 pebibyte", x.Pebi.ToString()); + Assert.Equal("0 tebibyte", x.Tebi.ToString()); + Assert.Equal("0.93 gibibyte", x.Gibi.ToString()); + Assert.Equal("953.67 mebibyte", x.Mebi.ToString()); + Assert.Equal("976,562.5 kibibyte", x.Kibi.ToString()); + + Assert.Equal("0 petabyte", x.Peta.ToString()); + Assert.Equal("0 terabyte", x.Tera.ToString()); + Assert.Equal("1 gigabyte", x.Giga.ToString()); + Assert.Equal("1,000 megabyte", x.Mega.ToString()); + Assert.Equal("1,000,000 kilobyte", x.Kilo.ToString()); + + Assert.Equal("1,000,000,000 byte", x.Unit.ToString()); + + TestOutput.WriteLine(x.ToAggregateString()); + } + } +} diff --git a/test/Cuemon.Core.Tests/DecimalPrefixTest.cs b/test/Cuemon.Core.Tests/DecimalPrefixTest.cs new file mode 100644 index 000000000..588a87ce4 --- /dev/null +++ b/test/Cuemon.Core.Tests/DecimalPrefixTest.cs @@ -0,0 +1,28 @@ +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon +{ + public class DecimalPrefixTest : Test + { + public DecimalPrefixTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void DecimalPrefix_ShouldVerifyMultiplePrefixConstants() + { + Assert.Equal(DecimalPrefix.Deca.Multiplier, 10d); + Assert.Equal(DecimalPrefix.Hecto.Multiplier, 100d); + Assert.Equal(DecimalPrefix.Kilo.Multiplier, 1000d); + Assert.Equal(DecimalPrefix.Mega.Multiplier, 1000000d); + Assert.Equal(DecimalPrefix.Giga.Multiplier, 1000000000d); + Assert.Equal(DecimalPrefix.Tera.Multiplier, 1000000000000d); + Assert.Equal(DecimalPrefix.Peta.Multiplier, 1000000000000000d); + Assert.Equal(DecimalPrefix.Exa.Multiplier, 1000000000000000000d); + Assert.Equal(DecimalPrefix.Zetta.Multiplier, 1000000000000000000000d); + Assert.Equal(DecimalPrefix.Yotta.Multiplier, 1000000000000000000000000d); + } + } +} \ No newline at end of file From 641f45a7745851d5762c923eb83ffbad84132dc7 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 4 Sep 2020 22:30:37 +0200 Subject: [PATCH 114/385] Added missing XML doc. --- src/Cuemon.Data.Integrity/EntityInfo.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Cuemon.Data.Integrity/EntityInfo.cs b/src/Cuemon.Data.Integrity/EntityInfo.cs index 1553703b3..6a2bf45b4 100644 --- a/src/Cuemon.Data.Integrity/EntityInfo.cs +++ b/src/Cuemon.Data.Integrity/EntityInfo.cs @@ -42,12 +42,28 @@ public EntityInfo(DateTime created, DateTime? modified, byte[] checksum, EntityD Validation = validation; } + /// + /// Gets a value from when data this resource represents was first created, expressed as the Coordinated Universal Time (UTC). + /// + /// The timestamp from when data this resource represents was first created. public DateTime Created { get; } + /// + /// Gets a value from when data this resource represents was last modified, expressed as the Coordinated Universal Time (UTC). + /// + /// The timestamp from when data this resource represents was last modified. public DateTime? Modified { get; } + /// + /// Gets a that represents the integrity of this instance. + /// + /// The checksum that represents the integrity of this instance. public HashResult Checksum { get; } + /// + /// Gets the validation strength of the integrity of this resource. + /// + /// The validation strength of the integrity of this resource. public EntityDataIntegrityValidation Validation { get; } } } \ No newline at end of file From bbe6679f44e0a13c924957ae43aa50b8fcd883f2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 5 Sep 2020 14:19:46 +0200 Subject: [PATCH 115/385] Retired. --- .../StandardizedDateTimeFormatPattern.cs | 25 ------ src/Cuemon.Core/StringFormatter.cs | 86 ------------------- 2 files changed, 111 deletions(-) delete mode 100644 src/Cuemon.Core/StandardizedDateTimeFormatPattern.cs delete mode 100644 src/Cuemon.Core/StringFormatter.cs diff --git a/src/Cuemon.Core/StandardizedDateTimeFormatPattern.cs b/src/Cuemon.Core/StandardizedDateTimeFormatPattern.cs deleted file mode 100644 index 5557b9f5b..000000000 --- a/src/Cuemon.Core/StandardizedDateTimeFormatPattern.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Cuemon -{ - /// - /// Defines some standardized patterns to use when formatting date- and time values. - /// - public enum StandardizedDateTimeFormatPattern - { - /// - /// Displays a date using the ISO8601 basic date format, eg.: YYYYMMDD. - /// - Iso8601CompleteDateBasic, - /// - /// Displays a date using the ISO8601 extended date format (human readable), eg.: YYYY-MM-DD. - /// - Iso8601CompleteDateExtended, - /// - /// Displays a date using the ISO8601 basic date format in conjunction with the ISO8601 time format, eg.: YYYYMMDDThhmmssTZD. - /// - Iso8601CompleteDateTimeBasic, - /// - /// Displays a date using the ISO8601 extended date format (human readable) in conjunction with the ISO8601 extended time format (human readable), eg.: YYYY-MM-DDThh:mm:ssTZD. - /// - Iso8601CompleteDateTimeExtended - } -} \ No newline at end of file diff --git a/src/Cuemon.Core/StringFormatter.cs b/src/Cuemon.Core/StringFormatter.cs deleted file mode 100644 index f5ca13ef5..000000000 --- a/src/Cuemon.Core/StringFormatter.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Globalization; - -namespace Cuemon -{ - /// - /// This utility class is designed to make related formating operations easier to work with. - /// - public static class StringFormatter - { - /// - /// Returns a string expression representing a standardized date- and time value. - /// - /// The value to be formatted. - /// The standardized patterns to apply on . - /// Returns a string expression representing a date- and time value. - public static string FromDateTime(DateTime value, StandardizedDateTimeFormatPattern pattern) - { - return FromDateTime(value, pattern, 0); - } - - /// - /// Returns a string expression representing a standardized date- and time value. - /// - /// The value to be formatted. - /// The standardized patterns to apply on . - /// The amount of fractional decimal places to apply to the string expression. - /// Returns a string expression representing a date- and time value. - public static string FromDateTime(DateTime value, StandardizedDateTimeFormatPattern pattern, byte fractionalDecimalPlaces) - { - switch (pattern) - { - case StandardizedDateTimeFormatPattern.Iso8601CompleteDateBasic: - return value.ToString("yyyyMMdd", CultureInfo.InvariantCulture); - case StandardizedDateTimeFormatPattern.Iso8601CompleteDateExtended: - return value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); - case StandardizedDateTimeFormatPattern.Iso8601CompleteDateTimeBasic: - return value.ToString(string.Format(CultureInfo.InvariantCulture, "yyyyMMddTHHmmss{0}{1}", fractionalDecimalPlaces == 0 ? "" : string.Format(CultureInfo.InvariantCulture, ".{0}", Generate.FixedString('f', fractionalDecimalPlaces)), value.Kind == DateTimeKind.Utc ? "Z" : "zz"), CultureInfo.InvariantCulture); - case StandardizedDateTimeFormatPattern.Iso8601CompleteDateTimeExtended: - return value.ToString(string.Format(CultureInfo.InvariantCulture, "yyyy-MM-ddTHH:mm:ss{0}{1}", fractionalDecimalPlaces == 0 ? "" : string.Format(CultureInfo.InvariantCulture, ".{0}", Generate.FixedString('f', fractionalDecimalPlaces)), value.Kind == DateTimeKind.Utc ? "Z" : "zz"), CultureInfo.InvariantCulture); - default: - throw new ArgumentOutOfRangeException(nameof(pattern)); - } - } - - /// - /// Returns a string expression representing a standardized date- and time value. - /// - /// The value to be formatted. - /// The standardized patterns to apply on . - /// Returns a string expression representing a date- and time value. - public static string FromDateTime(DateTime value, DateTimeFormatPattern pattern) - { - return FromDateTime(value, pattern, CultureInfo.InvariantCulture); - } - - /// - /// Returns a string expression representing a standardized date- and time value. - /// - /// The value to be formatted. - /// The standardized patterns to apply on . - /// An that supplies culture-specific formatting information. - /// Returns a string expression representing a date- and time value. - public static string FromDateTime(DateTime value, DateTimeFormatPattern pattern, IFormatProvider provider) - { - var formatInfo = DateTimeFormatInfo.GetInstance(provider); - switch (pattern) - { - case DateTimeFormatPattern.LongDate: - return value.ToString(formatInfo.LongDatePattern, formatInfo); - case DateTimeFormatPattern.LongDateTime: - return value.ToString(string.Format(formatInfo, "{0} {1}", formatInfo.LongDatePattern, formatInfo.LongTimePattern), formatInfo); - case DateTimeFormatPattern.LongTime: - return value.ToString(formatInfo.LongTimePattern, formatInfo); - case DateTimeFormatPattern.ShortDate: - return value.ToString(formatInfo.ShortDatePattern, formatInfo); - case DateTimeFormatPattern.ShortDateTime: - return value.ToString(string.Format(formatInfo, "{0} {1}", formatInfo.ShortDatePattern, formatInfo.ShortTimePattern), formatInfo); - case DateTimeFormatPattern.ShortTime: - return value.ToString(formatInfo.ShortTimePattern, formatInfo); - default: - throw new ArgumentOutOfRangeException(nameof(pattern)); - } - } - } -} \ No newline at end of file From 968c7032e9578c756f751352a86d3678eea343c9 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 5 Sep 2020 17:42:20 +0200 Subject: [PATCH 116/385] New addition to the Cuemon assmebly family. --- .../Cuemon.Extensions.Hosting.csproj | 27 ++++++++++++++++ .../HostEnvironmentExtensions.cs | 32 +++++++++++++++++++ .../HostingEnvironmentExtensions.cs | 32 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 4 +++ .../Properties/PackageReleaseNotes.txt | 22 +++++++++++++ 5 files changed, 117 insertions(+) create mode 100644 src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj create mode 100644 src/Cuemon.Extensions.Hosting/HostEnvironmentExtensions.cs create mode 100644 src/Cuemon.Extensions.Hosting/HostingEnvironmentExtensions.cs create mode 100644 src/Cuemon.Extensions.Hosting/Properties/AssemblyInfo.cs create mode 100644 src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt diff --git a/src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj b/src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj new file mode 100644 index 000000000..9071976b5 --- /dev/null +++ b/src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj @@ -0,0 +1,27 @@ + + + + netcoreapp3.0;netstandard2.0 + 1d0bdf91-e7c7-4cb4-a39d-e1a5374c5602 + + + + Cuemon.Extensions.Hosting + Cuemon.Extensions.Hosting + The Cuemon.Extensions.Hosting namespace contains extension methods and features related to the Microsoft.Extensions.Hosting namespace. + extension-methods extensions local-development non-production host hosting + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Cuemon.Extensions.Hosting/HostEnvironmentExtensions.cs b/src/Cuemon.Extensions.Hosting/HostEnvironmentExtensions.cs new file mode 100644 index 000000000..add72840e --- /dev/null +++ b/src/Cuemon.Extensions.Hosting/HostEnvironmentExtensions.cs @@ -0,0 +1,32 @@ +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Extensions.Hosting +{ + #if NETCOREAPP + /// + /// Extension methods for the interface. + /// + public static class HostEnvironmentExtensions + { + /// + /// Determines whether the specified is equal to LocalDevelopment. + /// + /// The to extend. + /// true if is LocalDevelopment; otherwise false + public static bool IsLocalDevelopment(this IHostEnvironment environment) + { + return environment.IsEnvironment("LocalDevelopment"); + } + + /// + /// Determines whether the specified is different from Production. + /// + /// The to extend. + /// true if is not Production; otherwise false + public static bool IsNonProduction(this IHostEnvironment environment) + { + return !environment.IsProduction(); + } + } + #endif +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Hosting/HostingEnvironmentExtensions.cs b/src/Cuemon.Extensions.Hosting/HostingEnvironmentExtensions.cs new file mode 100644 index 000000000..fe92e972d --- /dev/null +++ b/src/Cuemon.Extensions.Hosting/HostingEnvironmentExtensions.cs @@ -0,0 +1,32 @@ +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Extensions.Hosting +{ + #if NETSTANDARD + /// + /// Extension methods for the interface. + /// + public static class HostingEnvironmentExtensions + { + /// + /// Determines whether the specified is equal to LocalDevelopment. + /// + /// The to extend. + /// true if is LocalDevelopment; otherwise false + public static bool IsLocalDevelopment(this IHostingEnvironment environment) + { + return environment.IsEnvironment("LocalDevelopment"); + } + + /// + /// Determines whether the specified is different from Production. + /// + /// The to extend. + /// true if is not Production; otherwise false + public static bool IsNonProduction(this IHostingEnvironment environment) + { + return !environment.IsProduction(); + } + } + #endif +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Hosting/Properties/AssemblyInfo.cs b/src/Cuemon.Extensions.Hosting/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..c24d01879 --- /dev/null +++ b/src/Cuemon.Extensions.Hosting/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("e9bc4285-5627-4b81-b00b-712b38757ede")] \ No newline at end of file diff --git a/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..1ca063baa --- /dev/null +++ b/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt @@ -0,0 +1,22 @@ +6.0.0 +# New Features (NET Core) +- Added new extension methods for IHostEnvironment: IsLocalDevelopment and IsNonProduction + +# New Features (NET Standard) +- Added new extension methods for IHostingEnvironment: IsLocalDevelopment and IsNonProduction + +# Bug Fixes +- +- + +# Improvements +- +- + +# Quality Actions +- +- + +# Other Changes +- +- \ No newline at end of file From 16708534eb52391a173259db8521ccc3d85aa4f8 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 5 Sep 2020 18:22:13 +0200 Subject: [PATCH 117/385] Fixed format of package release notes. --- .../Properties/PackageReleaseNotes.txt | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt index 1ca063baa..026fef672 100644 --- a/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt @@ -1,22 +1,6 @@ -6.0.0 -# New Features (NET Core) -- Added new extension methods for IHostEnvironment: IsLocalDevelopment and IsNonProduction +Version: 6.0.0 +Availability: NET Standard 2.0, NET Core 3.0 -# New Features (NET Standard) -- Added new extension methods for IHostingEnvironment: IsLocalDevelopment and IsNonProduction - -# Bug Fixes -- -- - -# Improvements -- -- - -# Quality Actions -- -- - -# Other Changes -- -- \ No newline at end of file +# New Features +- Added extension methods for IHostEnvironment: IsLocalDevelopment and IsNonProduction +- Added extension methods for IHostingEnvironment: IsLocalDevelopment and IsNonProduction \ No newline at end of file From 8ae792e3836497e71bf2a90bf7bd7daa9d3db285 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 5 Sep 2020 18:27:09 +0200 Subject: [PATCH 118/385] Refactored and matured for initial release. --- .../Cuemon.Extensions.Xunit.csproj | 20 +------ src/Cuemon.Extensions.Xunit/HostTest.cs | 55 ------------------- .../Properties/AssemblyInfo.cs | 4 ++ .../Properties/PackageReleaseNotes.txt | 5 ++ src/Cuemon.Extensions.Xunit/Test.cs | 23 +++++++- 5 files changed, 34 insertions(+), 73 deletions(-) delete mode 100644 src/Cuemon.Extensions.Xunit/HostTest.cs create mode 100644 src/Cuemon.Extensions.Xunit/Properties/AssemblyInfo.cs create mode 100644 src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt diff --git a/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj b/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj index 174fd4f75..4e8c83ea7 100644 --- a/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj +++ b/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj @@ -1,31 +1,17 @@  - netstandard2.0;netcoreapp3.0 + netstandard2.0 0d0bdf91-e7c7-4cb4-a39d-e1a5374c5602 Cuemon.Extensions.Xunit Cuemon.Extensions.Xunit - The Cuemon.Extensions.Xunit namespace contains extension methods and features related to the Xunit.Abstractions namespace. - test host-test + The Cuemon.Extensions.Xunit namespace contains features that is related to the Xunit.Abstractions namespace. + test test-output test-disposable test-cleanup - - - - - - - - - - - - - - diff --git a/src/Cuemon.Extensions.Xunit/HostTest.cs b/src/Cuemon.Extensions.Xunit/HostTest.cs deleted file mode 100644 index ac4df2f58..000000000 --- a/src/Cuemon.Extensions.Xunit/HostTest.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.IO; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Xunit.Abstractions; - -namespace Cuemon.Extensions.Xunit -{ - public abstract class HostTest : Test - { - protected HostTest(ITestOutputHelper output = null) : base(output) - { - Host = new HostBuilder() - .ConfigureHostConfiguration(config => config.AddEnvironmentVariables("DOTNET_")) - .ConfigureAppConfiguration((context, config) => - { - config.SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", true, true) - .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) - .AddEnvironmentVariables(); - }) - .ConfigureServices((context, services) => - { - ConfigureServices(services); - Configuration = context.Configuration; - HostingEnvironment = context.HostingEnvironment; - ServiceProvider = services.BuildServiceProvider(); - }).Build(); - } - - public IHost Host { get; } - - public IServiceProvider ServiceProvider { get; private set; } - - public IConfiguration Configuration { get; private set; } - - #if NETSTANDARD - public IHostingEnvironment HostingEnvironment { get; private set; } - #elif NETCOREAPP - public IHostEnvironment HostingEnvironment { get; private set; } - #endif - - public abstract void ConfigureServices(IServiceCollection services); - - protected override void OnDisposeManagedResources() - { - if (ServiceProvider is ServiceProvider sp) - { - sp.Dispose(); - } - Host.Dispose(); - } - } -} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit/Properties/AssemblyInfo.cs b/src/Cuemon.Extensions.Xunit/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..5483dd4ec --- /dev/null +++ b/src/Cuemon.Extensions.Xunit/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("45986a3b-0eed-4d59-9e94-ea478d0d61ce")] \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..82cfa98d4 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt @@ -0,0 +1,5 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 + +# New Features +- Added Test class in the Cuemon.Extensions.Xunit namespace that represents the base class from which all implementations of unit testing should derive \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit/Test.cs b/src/Cuemon.Extensions.Xunit/Test.cs index 2665a5604..c441e9348 100644 --- a/src/Cuemon.Extensions.Xunit/Test.cs +++ b/src/Cuemon.Extensions.Xunit/Test.cs @@ -2,18 +2,39 @@ namespace Cuemon.Extensions.Xunit { + /// + /// Represents the base class from which all implementations of unit testing should derive. + /// + /// + /// public abstract class Test : Disposable { + /// + /// Initializes a new instance of the class. + /// + /// An implementation of the interface. + /// is initialized automatically in an xUnit project. protected Test(ITestOutputHelper output = null) { TestOutput = output; } + /// + /// Gets the console substitute to write out unit test information. + /// + /// The console substitute to write out unit test information. protected ITestOutputHelper TestOutput { get; } - protected bool HasTestOutputEnabled => TestOutput != null; + /// + /// Gets a value indicating whether has a reference to an implementation of . + /// + /// true if this instance has has a reference to an implementation of ; otherwise, false. + protected bool HasTestOutput => TestOutput != null; + /// + /// Called when this object is being disposed by either or having disposing set to true and is false. + /// protected override void OnDisposeManagedResources() { } From d0ffc8eae47b8f27258355a5432daba798e87176 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 5 Sep 2020 21:33:06 +0200 Subject: [PATCH 119/385] Added unit. --- .../Cuemon.Extensions.Xunit.Tests.csproj | 11 +++++ .../Cuemon.Extensions.Xunit.Tests/TestTest.cs | 48 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 test/Cuemon.Extensions.Xunit.Tests/Cuemon.Extensions.Xunit.Tests.csproj create mode 100644 test/Cuemon.Extensions.Xunit.Tests/TestTest.cs diff --git a/test/Cuemon.Extensions.Xunit.Tests/Cuemon.Extensions.Xunit.Tests.csproj b/test/Cuemon.Extensions.Xunit.Tests/Cuemon.Extensions.Xunit.Tests.csproj new file mode 100644 index 000000000..b5290d009 --- /dev/null +++ b/test/Cuemon.Extensions.Xunit.Tests/Cuemon.Extensions.Xunit.Tests.csproj @@ -0,0 +1,11 @@ + + + + Cuemon.Extensions.Xunit + + + + + + + \ No newline at end of file diff --git a/test/Cuemon.Extensions.Xunit.Tests/TestTest.cs b/test/Cuemon.Extensions.Xunit.Tests/TestTest.cs new file mode 100644 index 000000000..47181da10 --- /dev/null +++ b/test/Cuemon.Extensions.Xunit.Tests/TestTest.cs @@ -0,0 +1,48 @@ +using System; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Cuemon.Extensions.Xunit +{ + public class TestTest : Test + { + private const string ExpectedStringValue = "AllIsGood"; + private bool _onDisposeManagedResourcesCalled; + + public TestTest(ITestOutputHelper output = null) : base(output) + { + } + + [Fact] + public void Test_ShouldHaveTestOutput() + { + Assert.True(HasTestOutput); + Assert.IsAssignableFrom(TestOutput); + Assert.IsType(TestOutput); + } + + [Fact] + public void Test_ShouldInvokeDispose() + { + Assert.Equal(ExpectedStringValue, DisposeSensitiveMethod()); + Assert.False(_onDisposeManagedResourcesCalled); + Dispose(); + Assert.True(_onDisposeManagedResourcesCalled); + Assert.True(Disposed); + Assert.Throws(DisposeSensitiveMethod); + } + + public string DisposeSensitiveMethod() + { + if (Disposed) { throw new ObjectDisposedException(GetType().FullName); } + return ExpectedStringValue; + } + + protected override void OnDisposeManagedResources() + { + _onDisposeManagedResourcesCalled = true; + base.OnDisposeManagedResources(); + } + } +} \ No newline at end of file From 4bcb3169c11e61b0b6348525f0397ba8018e210a Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 16:42:01 +0200 Subject: [PATCH 120/385] Added support for unit testing (xUnit) that uses Microsoft Dependencny Injection. --- .../Cuemon.Extensions.Xunit.Hosting.csproj | 31 ++++++ .../HostFixture.cs | 98 +++++++++++++++++++ .../HostFixtureExtensions.cs | 10 ++ .../HostTest.cs | 78 +++++++++++++++ .../IHostFixture.cs | 59 +++++++++++ .../Properties/AssemblyInfo.cs | 4 + .../Properties/PackageReleaseNotes.txt | 7 ++ .../Assets/Correlation.cs | 15 +++ .../Assets/ScopedCorrelation.cs | 6 ++ .../Assets/SingletonCorrelation.cs | 6 ++ .../Assets/TransientCorrelation.cs | 6 ++ ...emon.Extensions.Hosting.Xunit.Tests.csproj | 25 +++++ .../HostTestTest.cs | 80 +++++++++++++++ .../appsettings.json | 3 + 14 files changed, 428 insertions(+) create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/HostFixtureExtensions.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/Properties/AssemblyInfo.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt create mode 100644 test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/Correlation.cs create mode 100644 test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/ScopedCorrelation.cs create mode 100644 test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/SingletonCorrelation.cs create mode 100644 test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/TransientCorrelation.cs create mode 100644 test/Cuemon.Extensions.Hosting.Xunit.Tests/Cuemon.Extensions.Hosting.Xunit.Tests.csproj create mode 100644 test/Cuemon.Extensions.Hosting.Xunit.Tests/HostTestTest.cs create mode 100644 test/Cuemon.Extensions.Hosting.Xunit.Tests/appsettings.json diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj new file mode 100644 index 000000000..538f3b388 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj @@ -0,0 +1,31 @@ + + + + netstandard2.0;netcoreapp3.0 + 1e0bdf91-e7c7-4cb4-a39d-e1a5374c5602 + + + + Cuemon.Extensions.Xunit.Hosting + Cuemon.Extensions.Xunit.Hosting + The Cuemon.Extensions.Xunit.Hosting namespace contains types that provides a uniform way of doing unit testing that is used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. + host-test + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs new file mode 100644 index 000000000..acff32475 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs @@ -0,0 +1,98 @@ +using System; +using System.IO; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Extensions.Xunit.Hosting +{ + /// + /// Provides a default implementation of the interface. + /// + /// + /// + public class HostFixture : Disposable, IHostFixture + { + /// + /// Initializes a new instance of the class. + /// + public HostFixture() + { + } + + /// + /// Creates and configures the of this instance. + /// + /// The type of the object that inherits from . + /// was added to support those cases where the caller is required in the host configuration. + public virtual void ConfigureHost(Type hostTestType) + { + Host = new HostBuilder() + .ConfigureHostConfiguration(config => config.AddEnvironmentVariables("DOTNET_")) + .ConfigureAppConfiguration((context, config) => + { + config.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) + .AddEnvironmentVariables(); + }) + .ConfigureServices((context, services) => + { + ConfigureServicesCallback(services); + Configuration = context.Configuration; + HostingEnvironment = context.HostingEnvironment; + ServiceProvider = services.BuildServiceProvider(); + }).Build(); + } + + /// + /// Gets or sets the delegate that adds services to the container. + /// + /// The delegate that adds services to the container. + public Action ConfigureServicesCallback { get; set; } + + /// + /// Gets the initialized by this instance. + /// + /// The initialized by this instance. + public IHost Host { get; private set; } + + /// + /// Gets the initialized by this instance. + /// + /// The initialized by this instance. + public IServiceProvider ServiceProvider { get; private set; } + + /// + /// Gets the initialized by this instance. + /// + /// The initialized by this instance. + public IConfiguration Configuration { get; private set; } + + #if NETSTANDARD + /// + /// Gets the initialized by this instance. + /// + /// The initialized by this instance. + public IHostingEnvironment HostingEnvironment { get; private set; } + #elif NETCOREAPP + /// + /// Gets the initialized by this instance. + /// + /// The initialized by this instance. + public IHostEnvironment HostingEnvironment { get; private set; } + #endif + + /// + /// Called when this object is being disposed by either or having disposing set to true and is false. + /// + protected override void OnDisposeManagedResources() + { + if (ServiceProvider is ServiceProvider sp) + { + sp.Dispose(); + } + Host?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostFixtureExtensions.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostFixtureExtensions.cs new file mode 100644 index 000000000..e720117a4 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostFixtureExtensions.cs @@ -0,0 +1,10 @@ +namespace Cuemon.Extensions.Xunit.Hosting +{ + internal static class HostFixtureExtensions + { + internal static bool HasValidState(this IHostFixture fixture) + { + return fixture.ConfigureServicesCallback != null && fixture.Host != null && fixture.ServiceProvider != null && fixture.Configuration != null; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs new file mode 100644 index 000000000..6c303e8be --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs @@ -0,0 +1,78 @@ +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Extensions.Xunit.Hosting +{ + + /// + /// Represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection, should derive. + /// + /// The type of the object that implements the interface. + /// + /// + /// The class needed to be designed in this rather complex way, as this is the only way that xUnit supports a shared context. The need for shared context is theoretical at best, but it does opt-in for Scoped instances. + public abstract class HostTest : Test, IClassFixture where T : class, IHostFixture + { + /// + /// Initializes a new instance of the class. + /// + /// An implementation of the interface. + /// An implementation of the interface. + protected HostTest(IHostFixture hostFixture, ITestOutputHelper output = null) : base(output) + { + Validator.ThrowIfNull(hostFixture, nameof(hostFixture)); + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureServicesCallback = ConfigureServices; + hostFixture.ConfigureHost(GetType()); + } + + Host = hostFixture.Host; + ServiceProvider = hostFixture.ServiceProvider; + Configuration = hostFixture.Configuration; + HostingEnvironment = hostFixture.HostingEnvironment; + } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IHost Host { get; } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IServiceProvider ServiceProvider { get; } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IConfiguration Configuration { get; } + + #if NETSTANDARD + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IHostingEnvironment HostingEnvironment { get; } + #elif NETCOREAPP + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IHostEnvironment HostingEnvironment { get; } + #endif + + /// + /// Adds services to the container. + /// + /// The collection of service descriptors. + public abstract void ConfigureServices(IServiceCollection services); + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs new file mode 100644 index 000000000..54dc8a8a5 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs @@ -0,0 +1,59 @@ +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Extensions.Xunit.Hosting +{ + /// + /// Provides a way to use Microsoft Dependency Injection in unit tests. + /// + /// + public interface IHostFixture : IDisposable + { + /// + /// Gets or sets the delegate that adds services to the container. + /// + /// The delegate that adds services to the container. + Action ConfigureServicesCallback { get; set; } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IHost Host { get; } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IServiceProvider ServiceProvider { get; } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IConfiguration Configuration { get; } + + #if NETSTANDARD + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IHostingEnvironment HostingEnvironment { get; } + #elif NETCOREAPP + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IHostEnvironment HostingEnvironment { get; } + #endif + + /// + /// Creates and configures the of this . + /// + /// The type of the object that inherits from . + /// was added to support those cases where the caller is required in the host configuration. + void ConfigureHost(Type hostTestType); + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Properties/AssemblyInfo.cs b/src/Cuemon.Extensions.Xunit.Hosting/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..891fbbf3f --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("413e5c92-990d-481d-99e9-e6214ae0d0d2")] \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..1df811961 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt @@ -0,0 +1,7 @@ +Version: 6.0.0 +Availability: NET Standard 2.0, NET Core 3.0 + +# New Features +- Added HostTest class in the Cuemon.Extensions.Xunit.Hosting namespace that represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection, should derive +- Added IHostFixture interface in the Cuemon.Extensions.Xunit.Hosting namespace that provides a way to use Microsoft Dependency Injection in unit tests +- Added HostFixture class in the Cuemon.Extensions.Xunit.Hosting namespace that provides a default implementation of the IHostFixture interface \ No newline at end of file diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/Correlation.cs b/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/Correlation.cs new file mode 100644 index 000000000..4d6706a2d --- /dev/null +++ b/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/Correlation.cs @@ -0,0 +1,15 @@ +using System; +using Cuemon.Messaging; + +namespace Cuemon.Extensions.Xunit.Hosting.Assets +{ + public abstract class Correlation : ICorrelation + { + protected Correlation() + { + CorrelationId = Guid.NewGuid().ToString("N"); + } + + public string CorrelationId { get; } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/ScopedCorrelation.cs b/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/ScopedCorrelation.cs new file mode 100644 index 000000000..0e2ba710e --- /dev/null +++ b/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/ScopedCorrelation.cs @@ -0,0 +1,6 @@ +namespace Cuemon.Extensions.Xunit.Hosting.Assets +{ + public sealed class ScopedCorrelation : Correlation + { + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/SingletonCorrelation.cs b/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/SingletonCorrelation.cs new file mode 100644 index 000000000..0ac794c95 --- /dev/null +++ b/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/SingletonCorrelation.cs @@ -0,0 +1,6 @@ +namespace Cuemon.Extensions.Xunit.Hosting.Assets +{ + public sealed class SingletonCorrelation : Correlation + { + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/TransientCorrelation.cs b/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/TransientCorrelation.cs new file mode 100644 index 000000000..f6728f356 --- /dev/null +++ b/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/TransientCorrelation.cs @@ -0,0 +1,6 @@ +namespace Cuemon.Extensions.Xunit.Hosting.Assets +{ + public sealed class TransientCorrelation : Correlation + { + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/Cuemon.Extensions.Hosting.Xunit.Tests.csproj b/test/Cuemon.Extensions.Hosting.Xunit.Tests/Cuemon.Extensions.Hosting.Xunit.Tests.csproj new file mode 100644 index 000000000..79aeb8c97 --- /dev/null +++ b/test/Cuemon.Extensions.Hosting.Xunit.Tests/Cuemon.Extensions.Hosting.Xunit.Tests.csproj @@ -0,0 +1,25 @@ + + + + Cuemon.Extensions.Xunit.Hosting + + + + + + + + + Always + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/HostTestTest.cs b/test/Cuemon.Extensions.Hosting.Xunit.Tests/HostTestTest.cs new file mode 100644 index 000000000..65ad8dfca --- /dev/null +++ b/test/Cuemon.Extensions.Hosting.Xunit.Tests/HostTestTest.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using Cuemon.Extensions.Xunit.Hosting.Assets; +using Cuemon.Messaging; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; +using Xunit.Priority; + +namespace Cuemon.Extensions.Xunit.Hosting +{ + [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] + public class HostTestTest : HostTest + { + private readonly Func> _correlationsFactory; + private static readonly ConcurrentBag ScopedCorrelations = new ConcurrentBag(); + + public HostTestTest(HostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _correlationsFactory = () => hostFixture.ServiceProvider.GetServices().ToList(); + } + + [Fact, Priority(1)] + public void Test_SingletonShouldBeSame() + { + ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); + var c1 = _correlationsFactory().Single(c => c is SingletonCorrelation); + var c2 = _correlationsFactory().Single(c => c is SingletonCorrelation); + Assert.Equal(c1.CorrelationId, c2.CorrelationId); + } + + [Fact, Priority(2)] + public void Test_TransientShouldBeDifferent() + { + ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); + var c1 = _correlationsFactory().Single(c => c is TransientCorrelation); + var c2 = _correlationsFactory().Single(c => c is TransientCorrelation); + Assert.NotEqual(c1.CorrelationId, c2.CorrelationId); + } + + [Fact, Priority(3)] + public void Test_ScopedShouldBeSame() + { + ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); + var c1 = _correlationsFactory().Single(c => c is ScopedCorrelation); + var c2 = _correlationsFactory().Single(c => c is ScopedCorrelation); + Assert.Equal(c1.CorrelationId, c2.CorrelationId); + } + + [Fact] + public void Test_ScopedShouldBeSameInLastTestRun() + { + var c1 = _correlationsFactory().Single(c => c is ScopedCorrelation); + if (ScopedCorrelations.IsEmpty) { return; } + Assert.Equal(3, ScopedCorrelations.Count); + Assert.All(ScopedCorrelations, c => Assert.Equal(c1.CorrelationId, c.CorrelationId)); + } + + [Fact] + public void Test_ShouldHaveConfigurationEntry() + { + Assert.Equal("xUnit", Configuration.GetSection("unitTestTool").Value); + } + + [Fact] + public void Test_ShouldHaveEnvironmentOfroduction() + { + Assert.Equal("Production", HostingEnvironment.EnvironmentName); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddTransient(); + services.AddScoped(); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/appsettings.json b/test/Cuemon.Extensions.Hosting.Xunit.Tests/appsettings.json new file mode 100644 index 000000000..ebbc38c92 --- /dev/null +++ b/test/Cuemon.Extensions.Hosting.Xunit.Tests/appsettings.json @@ -0,0 +1,3 @@ +{ + "unitTestTool": "xUnit" +} \ No newline at end of file From c901fb8257663b3192276449a3d0d86cb9201eac Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 16:42:13 +0200 Subject: [PATCH 121/385] Removed null. --- test/Cuemon.Extensions.Xunit.Tests/TestTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Cuemon.Extensions.Xunit.Tests/TestTest.cs b/test/Cuemon.Extensions.Xunit.Tests/TestTest.cs index 47181da10..a9deab856 100644 --- a/test/Cuemon.Extensions.Xunit.Tests/TestTest.cs +++ b/test/Cuemon.Extensions.Xunit.Tests/TestTest.cs @@ -10,7 +10,7 @@ public class TestTest : Test private const string ExpectedStringValue = "AllIsGood"; private bool _onDisposeManagedResourcesCalled; - public TestTest(ITestOutputHelper output = null) : base(output) + public TestTest(ITestOutputHelper output) : base(output) { } From 5a6188e04bc8cabd114841ddb4ea2f08c315aecb Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 16:42:40 +0200 Subject: [PATCH 122/385] Changed package description a little. --- .../Cuemon.Extensions.Newtonsoft.Json.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj index e9710f861..9d07231d5 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Newtonsoft.Json Cuemon.Extensions.Newtonsoft.Json - The Cuemon.Extensions.Newtonsoft.Json namespace contains extension methods and features related to the Newtonsoft.Json namespace. + The Cuemon.Extensions.Newtonsoft.Json namespace contains extension methods and features that is related to the Newtonsoft.Json namespace. extension-methods extensions jdata jdata-result json-converter json-formatter From bb08d53087b498daf2fd8b8bfb5d415883071beb Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 16:42:55 +0200 Subject: [PATCH 123/385] Changed package description a little. --- src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj b/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj index 4e8c83ea7..000ded379 100644 --- a/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj +++ b/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Xunit Cuemon.Extensions.Xunit - The Cuemon.Extensions.Xunit namespace contains features that is related to the Xunit.Abstractions namespace. + The Cuemon.Extensions.Xunit namespace contains types that provides a uniform way of doing unit testing. The namespace relates to the Xunit.Abstractions namespace. test test-output test-disposable test-cleanup From a9fdb7ea7a2c2090108ad2089658ce052a123633 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 16:43:47 +0200 Subject: [PATCH 124/385] Added template for package release notes. --- src/Cuemon.Core/Cuemon.Core.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/Cuemon.Core/Properties/PackageReleaseNotes.txt diff --git a/src/Cuemon.Core/Cuemon.Core.csproj b/src/Cuemon.Core/Cuemon.Core.csproj index b473a931d..96fbd1f0d 100644 --- a/src/Cuemon.Core/Cuemon.Core.csproj +++ b/src/Cuemon.Core/Cuemon.Core.csproj @@ -9,7 +9,7 @@ Cuemon Cuemon.Core Cuemon - The Cuemon namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. + The Cuemon namespace contains fundamental factories, classes and base classes that define invaluable reference and value types that greatly extends the System namespace. Abundant support for delegates and functional programming. action-factory bit-unit byte-unit calculator configure configure-revert configure-exchange configurable condition options-pattern data-reader decorator delimited-string disposable finalize-disposable safe-invoke safe-invoke-async func-factory patterns reference-project clean-architecture clean-code task-action-factory task-func-factory template template-factory time-range time-unit validator guard text-encoding parser-factory security aes-cryptor cyclic-redundancy-check fowler-noll-vo-hash hash-factory hash-result hmac-message-digest hmac-secure-hash-algorithm keyed-crypto-hash keyed-crypto-algorithm message-digest non-crypto-algorithm secure-hash-algorithm unkeyed-crypto-hash diff --git a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..a3a8fa38a --- /dev/null +++ b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt @@ -0,0 +1,28 @@ +6.0.0 +# Upgrade Steps +- [ACTION REQUIRED] +- + +# Breaking Changes +- REMOVED StringFormatter class in the Cuemon namespace +- REMOVED StandardizedDateTimeFormatPattern enum in the Cuemon namespace + +# New Features +- +- + +# Bug Fixes +- +- + +# Improvements +- +- + +# Quality Actions +- +- + +# Other Changes +- +- \ No newline at end of file From fc744caed89fae7262d872d65008cb74653d998d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 16:44:00 +0200 Subject: [PATCH 125/385] Added ingores for DocFx. --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 381422d9b..990d5bc38 100644 --- a/.gitignore +++ b/.gitignore @@ -219,3 +219,8 @@ ModelManifest.xml # Resharper *.DotSettings + +# DocFx +/docfx/wwwroot +/docfx/api/**/*.yml +/docfx/**/*.manifest From f17eb2f2250a7ddcf82c0c7b0d088af9af8b3dde Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 16:45:38 +0200 Subject: [PATCH 126/385] Changed to build Cuemon.Extensions.Xunit.Hosting for both netstandard2 and netcoreapp3. --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b6ccbd3bd..ae1eb8c6c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -92,7 +92,7 @@ jobs: projects: | src/**/Cuemon.AspNetCore*.csproj src/**/Cuemon.Extensions.AspNetCore*.csproj - src/**/Cuemon.Extensions.Xunit.csproj + src/**/Cuemon.Extensions.Xunit.Hosting.csproj arguments: '--configuration $(BuildConfiguration) --no-restore --framework netcoreapp3.0' workingDirectory: '$(BuildSource)' From 7abb188eb3910456c090e8b493ade920c3df5fdb Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 16:46:09 +0200 Subject: [PATCH 127/385] Added support for reading package release notes externally. Inspiration from: https://dev.to/j_sakamoto/writing-a-nuget-package-release-notes-in-an-outside-of-a-csproj-file-3f94 --- Directory.Build.targets | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 45a61eebb..4dbcaa36d 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -8,12 +8,12 @@ - + \ No newline at end of file From 84b62568085529730744020e208815f5f4b9ead2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 16:46:35 +0200 Subject: [PATCH 128/385] Updated with new project references. --- Cuemon.sln | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/Cuemon.sln b/Cuemon.sln index 849d431d7..5ccfed1d8 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -107,7 +107,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Diagnostics.Tests", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Security.Cryptography", "src\Cuemon.Security.Cryptography\Cuemon.Security.Cryptography.csproj", "{1B0BDF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Security.Cryptography.Tests", "test\Cuemon.Security.Cryptography.Tests\Cuemon.Security.Cryptography.Tests.csproj", "{5D67081C-4458-41AA-A1F5-FAC974D29FDF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Security.Cryptography.Tests", "test\Cuemon.Security.Cryptography.Tests\Cuemon.Security.Cryptography.Tests.csproj", "{5D67081C-4458-41AA-A1F5-FAC974D29FDF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Xunit.Hosting", "src\Cuemon.Extensions.Xunit.Hosting\Cuemon.Extensions.Xunit.Hosting.csproj", "{D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Hosting", "src\Cuemon.Extensions.Hosting\Cuemon.Extensions.Hosting.csproj", "{87BB6200-C51C-4085-8CE3-83B3C7A4FD8F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Xunit.Tests", "test\Cuemon.Extensions.Xunit.Tests\Cuemon.Extensions.Xunit.Tests.csproj", "{2108E7E7-F002-481C-B17F-918E76D98378}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Hosting.Xunit.Tests", "test\Cuemon.Extensions.Hosting.Xunit.Tests\Cuemon.Extensions.Hosting.Xunit.Tests.csproj", "{86B43822-0733-416E-8DA2-666C5657974F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -319,6 +327,22 @@ Global {5D67081C-4458-41AA-A1F5-FAC974D29FDF}.Debug|Any CPU.Build.0 = Debug|Any CPU {5D67081C-4458-41AA-A1F5-FAC974D29FDF}.Release|Any CPU.ActiveCfg = Release|Any CPU {5D67081C-4458-41AA-A1F5-FAC974D29FDF}.Release|Any CPU.Build.0 = Release|Any CPU + {D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9}.Release|Any CPU.Build.0 = Release|Any CPU + {87BB6200-C51C-4085-8CE3-83B3C7A4FD8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87BB6200-C51C-4085-8CE3-83B3C7A4FD8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87BB6200-C51C-4085-8CE3-83B3C7A4FD8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87BB6200-C51C-4085-8CE3-83B3C7A4FD8F}.Release|Any CPU.Build.0 = Release|Any CPU + {2108E7E7-F002-481C-B17F-918E76D98378}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2108E7E7-F002-481C-B17F-918E76D98378}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2108E7E7-F002-481C-B17F-918E76D98378}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2108E7E7-F002-481C-B17F-918E76D98378}.Release|Any CPU.Build.0 = Release|Any CPU + {86B43822-0733-416E-8DA2-666C5657974F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86B43822-0733-416E-8DA2-666C5657974F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86B43822-0733-416E-8DA2-666C5657974F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86B43822-0733-416E-8DA2-666C5657974F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -375,6 +399,10 @@ Global {06559CB0-899C-4B48-AFB8-633CBF97A766} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {1B0BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} {5D67081C-4458-41AA-A1F5-FAC974D29FDF} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} + {D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} + {87BB6200-C51C-4085-8CE3-83B3C7A4FD8F} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} + {2108E7E7-F002-481C-B17F-918E76D98378} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} + {86B43822-0733-416E-8DA2-666C5657974F} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2A848386-B682-4F6D-8254-B5F6247C3054} From fb3d98a5d410738882385d9c208552685e18f5fd Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 16:49:35 +0200 Subject: [PATCH 129/385] Initial DocFx setup. --- docfx/api/aspnet/ext/index.md | 45 + docfx/api/aspnet/index.md | 45 + docfx/api/dotnet/ext/index.md | 45 + docfx/api/dotnet/index.md | 45 + docfx/api/index.md | 45 + .../Cuemon.AspNetCore.Authentication.md | 5 + .../namespaces/Cuemon.AspNetCore.Builder.md | 5 + .../Cuemon.AspNetCore.Configuration.md | 5 + .../namespaces/Cuemon.AspNetCore.Hosting.md | 5 + .../Cuemon.AspNetCore.Http.Headers.md | 5 + .../Cuemon.AspNetCore.Http.Throttling.md | 5 + .../api/namespaces/Cuemon.AspNetCore.Http.md | 5 + ...Cuemon.AspNetCore.Mvc.Filters.Cacheable.md | 5 + ...emon.AspNetCore.Mvc.Filters.Diagnostics.md | 5 + .../Cuemon.AspNetCore.Mvc.Filters.Headers.md | 5 + ...mon.AspNetCore.Mvc.Filters.ModelBinding.md | 5 + ...uemon.AspNetCore.Mvc.Filters.Throttling.md | 5 + .../Cuemon.AspNetCore.Mvc.Filters.md | 5 + docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md | 5 + .../Cuemon.AspNetCore.Razor.TagHelpers.md | 5 + .../api/namespaces/Cuemon.AspNetCore.Razor.md | 5 + docfx/api/namespaces/Cuemon.AspNetCore.md | 5 + .../namespaces/Cuemon.Collections.Generic.md | 5 + docfx/api/namespaces/Cuemon.Collections.md | 5 + docfx/api/namespaces/Cuemon.Configuration.md | 5 + docfx/api/namespaces/Cuemon.Data.Integrity.md | 5 + docfx/api/namespaces/Cuemon.Data.SqlClient.md | 5 + docfx/api/namespaces/Cuemon.Data.Xml.md | 5 + docfx/api/namespaces/Cuemon.Data.md | 5 + docfx/api/namespaces/Cuemon.Diagnostics.md | 5 + .../Cuemon.Extensions.AspNetCore.Builder.md | 5 + ...on.Extensions.AspNetCore.Data.Integrity.md | 5 + ...n.Extensions.AspNetCore.Http.Throttling.md | 5 + .../Cuemon.Extensions.AspNetCore.Http.md | 5 + ...Extensions.AspNetCore.Mvc.Configuration.md | 5 + ...nsions.AspNetCore.Mvc.Filters.Cacheable.md | 5 + ...ions.AspNetCore.Mvc.Filters.Diagnostics.md | 5 + ...c.Formatters.Newtonsoft.Json.Converters.md | 5 + ...pNetCore.Mvc.Formatters.Newtonsoft.Json.md | 5 + ...spNetCore.Mvc.Formatters.Xml.Converters.md | 5 + ...xtensions.AspNetCore.Mvc.Formatters.Xml.md | 5 + ...mon.Extensions.AspNetCore.Mvc.Rendering.md | 5 + .../Cuemon.Extensions.AspNetCore.Mvc.md | 5 + .../Cuemon.Extensions.AspNetCore.md | 5 + .../Cuemon.Extensions.Collections.Generic.md | 5 + ...emon.Extensions.Collections.Specialized.md | 5 + .../Cuemon.Extensions.Data.Integrity.md | 5 + .../api/namespaces/Cuemon.Extensions.Data.md | 5 + .../Cuemon.Extensions.DependencyInjection.md | 5 + .../Cuemon.Extensions.Diagnostics.md | 5 + .../namespaces/Cuemon.Extensions.Hosting.md | 7 + docfx/api/namespaces/Cuemon.Extensions.IO.md | 5 + .../namespaces/Cuemon.Extensions.Net.Http.md | 5 + .../Cuemon.Extensions.Net.Security.md | 5 + docfx/api/namespaces/Cuemon.Extensions.Net.md | 5 + ...n.Extensions.Newtonsoft.Json.Converters.md | 5 + ....Extensions.Newtonsoft.Json.Diagnostics.md | 5 + ...n.Extensions.Newtonsoft.Json.Formatters.md | 5 + .../Cuemon.Extensions.Newtonsoft.Json.md | 5 + .../Cuemon.Extensions.Reflection.md | 5 + .../api/namespaces/Cuemon.Extensions.Text.md | 5 + .../Cuemon.Extensions.Threading.Tasks.md | 5 + .../namespaces/Cuemon.Extensions.Threading.md | 5 + .../namespaces/Cuemon.Extensions.Xml.Linq.md | 5 + ...Extensions.Xml.Serialization.Converters.md | 5 + ...xtensions.Xml.Serialization.Diagnostics.md | 5 + .../Cuemon.Extensions.Xml.Serialization.md | 5 + docfx/api/namespaces/Cuemon.Extensions.Xml.md | 5 + .../Cuemon.Extensions.Xunit.Hosting.md | 7 + .../api/namespaces/Cuemon.Extensions.Xunit.md | 7 + docfx/api/namespaces/Cuemon.Extensions.md | 5 + docfx/api/namespaces/Cuemon.Globalization.md | 5 + docfx/api/namespaces/Cuemon.IO.md | 5 + docfx/api/namespaces/Cuemon.Messaging.md | 5 + docfx/api/namespaces/Cuemon.Net.Http.md | 5 + docfx/api/namespaces/Cuemon.Net.Mail.md | 5 + docfx/api/namespaces/Cuemon.Net.md | 5 + docfx/api/namespaces/Cuemon.Reflection.md | 5 + docfx/api/namespaces/Cuemon.Resilience.md | 5 + .../api/namespaces/Cuemon.Runtime.Caching.md | 5 + ...Cuemon.Runtime.Serialization.Formatters.md | 5 + .../Cuemon.Runtime.Serialization.md | 5 + docfx/api/namespaces/Cuemon.Runtime.md | 5 + .../Cuemon.Security.Cryptography.md | 5 + docfx/api/namespaces/Cuemon.Security.md | 5 + docfx/api/namespaces/Cuemon.Text.md | 5 + docfx/api/namespaces/Cuemon.Threading.md | 5 + .../Cuemon.Xml.Serialization.Converters.md | 5 + .../Cuemon.Xml.Serialization.Formatters.md | 5 + docfx/api/namespaces/Cuemon.Xml.XPath.md | 5 + docfx/api/namespaces/Cuemon.Xml.md | 5 + docfx/api/namespaces/Cuemon.md | 5 + docfx/docfx.json | 859 ++++++++++++++++++ docfx/filterConfig.yml | 4 + docfx/images/32x32.png | Bin 0 -> 1123 bytes docfx/images/50x50.png | Bin 0 -> 283 bytes docfx/images/favicon.ico | Bin 0 -> 360414 bytes docfx/index.md | 518 +++++++++++ docfx/templates/cuemon/index.html.tmpl | 17 + docfx/templates/cuemon/layout/_master.tmpl | 65 ++ .../cuemon/partials/head.tmpl.partial | 28 + docfx/templates/cuemon/styles/main.css | 60 ++ docfx/toc.yml | 15 + 103 files changed, 2232 insertions(+) create mode 100644 docfx/api/aspnet/ext/index.md create mode 100644 docfx/api/aspnet/index.md create mode 100644 docfx/api/dotnet/ext/index.md create mode 100644 docfx/api/dotnet/index.md create mode 100644 docfx/api/index.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Authentication.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Builder.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Http.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Razor.TagHelpers.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Razor.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.md create mode 100644 docfx/api/namespaces/Cuemon.Collections.Generic.md create mode 100644 docfx/api/namespaces/Cuemon.Collections.md create mode 100644 docfx/api/namespaces/Cuemon.Configuration.md create mode 100644 docfx/api/namespaces/Cuemon.Data.Integrity.md create mode 100644 docfx/api/namespaces/Cuemon.Data.SqlClient.md create mode 100644 docfx/api/namespaces/Cuemon.Data.Xml.md create mode 100644 docfx/api/namespaces/Cuemon.Data.md create mode 100644 docfx/api/namespaces/Cuemon.Diagnostics.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Data.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Hosting.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.IO.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Net.Http.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Net.Security.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Net.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Reflection.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Text.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Threading.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Xml.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Xunit.md create mode 100644 docfx/api/namespaces/Cuemon.Extensions.md create mode 100644 docfx/api/namespaces/Cuemon.Globalization.md create mode 100644 docfx/api/namespaces/Cuemon.IO.md create mode 100644 docfx/api/namespaces/Cuemon.Messaging.md create mode 100644 docfx/api/namespaces/Cuemon.Net.Http.md create mode 100644 docfx/api/namespaces/Cuemon.Net.Mail.md create mode 100644 docfx/api/namespaces/Cuemon.Net.md create mode 100644 docfx/api/namespaces/Cuemon.Reflection.md create mode 100644 docfx/api/namespaces/Cuemon.Resilience.md create mode 100644 docfx/api/namespaces/Cuemon.Runtime.Caching.md create mode 100644 docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md create mode 100644 docfx/api/namespaces/Cuemon.Runtime.Serialization.md create mode 100644 docfx/api/namespaces/Cuemon.Runtime.md create mode 100644 docfx/api/namespaces/Cuemon.Security.Cryptography.md create mode 100644 docfx/api/namespaces/Cuemon.Security.md create mode 100644 docfx/api/namespaces/Cuemon.Text.md create mode 100644 docfx/api/namespaces/Cuemon.Threading.md create mode 100644 docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md create mode 100644 docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md create mode 100644 docfx/api/namespaces/Cuemon.Xml.XPath.md create mode 100644 docfx/api/namespaces/Cuemon.Xml.md create mode 100644 docfx/api/namespaces/Cuemon.md create mode 100644 docfx/docfx.json create mode 100644 docfx/filterConfig.yml create mode 100644 docfx/images/32x32.png create mode 100644 docfx/images/50x50.png create mode 100644 docfx/images/favicon.ico create mode 100644 docfx/index.md create mode 100644 docfx/templates/cuemon/index.html.tmpl create mode 100644 docfx/templates/cuemon/layout/_master.tmpl create mode 100644 docfx/templates/cuemon/partials/head.tmpl.partial create mode 100644 docfx/templates/cuemon/styles/main.css create mode 100644 docfx/toc.yml diff --git a/docfx/api/aspnet/ext/index.md b/docfx/api/aspnet/ext/index.md new file mode 100644 index 000000000..5939a2736 --- /dev/null +++ b/docfx/api/aspnet/ext/index.md @@ -0,0 +1,45 @@ +--- +uid: extensions-aspnet-md +title: Extensions for ASP.NET Core API Reference +--- +## Cuemon Extensions for ASP.NET Core API Reference + +The **Cuemon** assembly family provides both enhancements and extension methods to these namespaces of [Microsoft .NET Standard](https://docs.microsoft.com/en-us/dotnet/api/?view=netstandard-2.0): + ++ System ++ System.Collections ++ System.Collections.Concurrent ++ System.Collections.Generic ++ System.Collections.ObjectModel ++ System.Collections.Specialized ++ System.ComponentModel ++ System.Configuration ++ System.Data ++ System.Data.Common ++ System.Diagnostics ++ System.Globalization ++ System.Linq ++ System.IO ++ System.Net.Httpp ++ System.Net.Http.Headers ++ System.Net.Mail ++ System.Reflection ++ System.Runtime.CompilerServices ++ System.Security.Cryptography ++ System.Text ++ System.Threading ++ System.Threading.Tasks ++ System.Xml ++ System.Xml.Serialization ++ System.Xml.XPath + +### Cuemon.Data +[NS-2.0-API](/api/cuemon/data/index.html) + +### Cuemon.Diagnostics +[NS-2.0-API](/api/cuemon/diagnostics/index.html) + +### Cuemon.Xml +[NS-2.0-API](/api/core/netstandard2.0/Cuemon.Xml.html) + +[![Build status](https://gimlichael.visualstudio.com/CuemonCore/_apis/build/status/CuemonCore%20-%20Development%20-%20CI)](https://gimlichael.visualstudio.com/CuemonCore/_build/latest?definitionId=1) \ No newline at end of file diff --git a/docfx/api/aspnet/index.md b/docfx/api/aspnet/index.md new file mode 100644 index 000000000..ef0528f00 --- /dev/null +++ b/docfx/api/aspnet/index.md @@ -0,0 +1,45 @@ +--- +uid: aspnet-md +title: ASP.NET Core API Reference +--- +## Cuemon ASP.NET Core API Reference + +The **Cuemon** assembly family provides both enhancements and extension methods to these namespaces of [Microsoft .NET Standard](https://docs.microsoft.com/en-us/dotnet/api/?view=netstandard-2.0): + ++ System ++ System.Collections ++ System.Collections.Concurrent ++ System.Collections.Generic ++ System.Collections.ObjectModel ++ System.Collections.Specialized ++ System.ComponentModel ++ System.Configuration ++ System.Data ++ System.Data.Common ++ System.Diagnostics ++ System.Globalization ++ System.Linq ++ System.IO ++ System.Net.Httpp ++ System.Net.Http.Headers ++ System.Net.Mail ++ System.Reflection ++ System.Runtime.CompilerServices ++ System.Security.Cryptography ++ System.Text ++ System.Threading ++ System.Threading.Tasks ++ System.Xml ++ System.Xml.Serialization ++ System.Xml.XPath + +### Cuemon.Data +[NS-2.0-API](/api/cuemon/data/index.html) + +### Cuemon.Diagnostics +[NS-2.0-API](/api/cuemon/diagnostics/index.html) + +### Cuemon.Xml +[NS-2.0-API](/api/core/netstandard2.0/Cuemon.Xml.html) + +[![Build status](https://gimlichael.visualstudio.com/CuemonCore/_apis/build/status/CuemonCore%20-%20Development%20-%20CI)](https://gimlichael.visualstudio.com/CuemonCore/_build/latest?definitionId=1) \ No newline at end of file diff --git a/docfx/api/dotnet/ext/index.md b/docfx/api/dotnet/ext/index.md new file mode 100644 index 000000000..382653803 --- /dev/null +++ b/docfx/api/dotnet/ext/index.md @@ -0,0 +1,45 @@ +--- +uid: extensions-dotnet-md +title: Extensions for Core API Reference +--- +## Cuemon Extensions for Core API Reference + +The **Cuemon** assembly family provides both enhancements and extension methods to these namespaces of [Microsoft .NET Standard](https://docs.microsoft.com/en-us/dotnet/api/?view=netstandard-2.0): + ++ System ++ System.Collections ++ System.Collections.Concurrent ++ System.Collections.Generic ++ System.Collections.ObjectModel ++ System.Collections.Specialized ++ System.ComponentModel ++ System.Configuration ++ System.Data ++ System.Data.Common ++ System.Diagnostics ++ System.Globalization ++ System.Linq ++ System.IO ++ System.Net.Httpp ++ System.Net.Http.Headers ++ System.Net.Mail ++ System.Reflection ++ System.Runtime.CompilerServices ++ System.Security.Cryptography ++ System.Text ++ System.Threading ++ System.Threading.Tasks ++ System.Xml ++ System.Xml.Serialization ++ System.Xml.XPath + +### Cuemon.Data +[NS-2.0-API](/api/cuemon/data/index.html) + +### Cuemon.Diagnostics +[NS-2.0-API](/api/cuemon/diagnostics/index.html) + +### Cuemon.Xml +[NS-2.0-API](/api/core/netstandard2.0/Cuemon.Xml.html) + +[![Build status](https://gimlichael.visualstudio.com/CuemonCore/_apis/build/status/CuemonCore%20-%20Development%20-%20CI)](https://gimlichael.visualstudio.com/CuemonCore/_build/latest?definitionId=1) \ No newline at end of file diff --git a/docfx/api/dotnet/index.md b/docfx/api/dotnet/index.md new file mode 100644 index 000000000..a6766c963 --- /dev/null +++ b/docfx/api/dotnet/index.md @@ -0,0 +1,45 @@ +--- +uid: dotnet-md +title: Core API Reference +--- +## Cuemon Core API Reference + +The **Cuemon** assembly family provides both enhancements and extension methods to these namespaces of [Microsoft .NET Standard](https://docs.microsoft.com/en-us/dotnet/api/?view=netstandard-2.0): + ++ System ++ System.Collections ++ System.Collections.Concurrent ++ System.Collections.Generic ++ System.Collections.ObjectModel ++ System.Collections.Specialized ++ System.ComponentModel ++ System.Configuration ++ System.Data ++ System.Data.Common ++ System.Diagnostics ++ System.Globalization ++ System.Linq ++ System.IO ++ System.Net.Httpp ++ System.Net.Http.Headers ++ System.Net.Mail ++ System.Reflection ++ System.Runtime.CompilerServices ++ System.Security.Cryptography ++ System.Text ++ System.Threading ++ System.Threading.Tasks ++ System.Xml ++ System.Xml.Serialization ++ System.Xml.XPath + +### Cuemon.Data +[NS-2.0-API](/api/cuemon/data/index.html) + +### Cuemon.Diagnostics +[NS-2.0-API](/api/cuemon/diagnostics/index.html) + +### Cuemon.Xml +[NS-2.0-API](/api/core/netstandard2.0/Cuemon.Xml.html) + +[![Build status](https://gimlichael.visualstudio.com/CuemonCore/_apis/build/status/CuemonCore%20-%20Development%20-%20CI)](https://gimlichael.visualstudio.com/CuemonCore/_build/latest?definitionId=1) \ No newline at end of file diff --git a/docfx/api/index.md b/docfx/api/index.md new file mode 100644 index 000000000..7ff9326b1 --- /dev/null +++ b/docfx/api/index.md @@ -0,0 +1,45 @@ +--- +uid: concept-md +title: Cuemon Concept Reference +--- +## Cuemon Concept Reference + +The **Cuemon** assembly family provides both enhancements and extension methods to these namespaces of [Microsoft .NET Standard](https://docs.microsoft.com/en-us/dotnet/api/?view=netstandard-2.0): + ++ System ++ System.Collections ++ System.Collections.Concurrent ++ System.Collections.Generic ++ System.Collections.ObjectModel ++ System.Collections.Specialized ++ System.ComponentModel ++ System.Configuration ++ System.Data ++ System.Data.Common ++ System.Diagnostics ++ System.Globalization ++ System.Linq ++ System.IO ++ System.Net.Httpp ++ System.Net.Http.Headers ++ System.Net.Mail ++ System.Reflection ++ System.Runtime.CompilerServices ++ System.Security.Cryptography ++ System.Text ++ System.Threading ++ System.Threading.Tasks ++ System.Xml ++ System.Xml.Serialization ++ System.Xml.XPath + +### Cuemon.Data +[NS-2.0-API](/api/cuemon/data/index.html) + +### Cuemon.Diagnostics +[NS-2.0-API](/api/cuemon/diagnostics/index.html) + +### Cuemon.Xml +[NS-2.0-API](/api/core/netstandard2.0/Cuemon.Xml.html) + +[![Build status](https://gimlichael.visualstudio.com/CuemonCore/_apis/build/status/CuemonCore%20-%20Development%20-%20CI)](https://gimlichael.visualstudio.com/CuemonCore/_build/latest?definitionId=1) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.md b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.md new file mode 100644 index 000000000..b99877655 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Authentication +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Builder.md b/docfx/api/namespaces/Cuemon.AspNetCore.Builder.md new file mode 100644 index 000000000..ed6dadbf2 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Builder.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Builder +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md b/docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md new file mode 100644 index 000000000..05bc718a6 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Configuration +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md b/docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md new file mode 100644 index 000000000..ce0004378 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Hosting +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md new file mode 100644 index 000000000..6248be9d3 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Http.Headers +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md new file mode 100644 index 000000000..33207b750 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Http.Throttling +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Http.md b/docfx/api/namespaces/Cuemon.AspNetCore.Http.md new file mode 100644 index 000000000..fc382f936 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Http.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Http +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md new file mode 100644 index 000000000..3b375e569 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Mvc.Filters.Cacheable +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md new file mode 100644 index 000000000..1ad5c5ffd --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Mvc.Filters.Diagnostics +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md new file mode 100644 index 000000000..37a99e300 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Mvc.Filters.Headers +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md new file mode 100644 index 000000000..bed19f8c7 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Mvc.Filters.ModelBinding +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md new file mode 100644 index 000000000..29d207362 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Mvc.Filters.Throttling +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md new file mode 100644 index 000000000..282f3a9c3 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Mvc.Filters +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md new file mode 100644 index 000000000..a205a70a2 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Mvc +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Razor.TagHelpers.md b/docfx/api/namespaces/Cuemon.AspNetCore.Razor.TagHelpers.md new file mode 100644 index 000000000..bf09c1294 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Razor.TagHelpers.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Razor.TagHelpers +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Razor.md b/docfx/api/namespaces/Cuemon.AspNetCore.Razor.md new file mode 100644 index 000000000..16fd9d1b2 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Razor.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore.Razor +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.md b/docfx/api/namespaces/Cuemon.AspNetCore.md new file mode 100644 index 000000000..23ca69cbc --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.AspNetCore +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Collections.Generic.md b/docfx/api/namespaces/Cuemon.Collections.Generic.md new file mode 100644 index 000000000..17963039f --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Collections.Generic.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Collections.Generic +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Collections.md b/docfx/api/namespaces/Cuemon.Collections.md new file mode 100644 index 000000000..5f42234e2 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Collections.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Collections +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Configuration.md b/docfx/api/namespaces/Cuemon.Configuration.md new file mode 100644 index 000000000..2856100c8 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Configuration.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Configuration +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Data.Integrity.md b/docfx/api/namespaces/Cuemon.Data.Integrity.md new file mode 100644 index 000000000..9ec436de3 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Data.Integrity.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Data.Integrity +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Data.SqlClient.md b/docfx/api/namespaces/Cuemon.Data.SqlClient.md new file mode 100644 index 000000000..dd2e8631a --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Data.SqlClient.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Data.SqlClient +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Data.Xml.md b/docfx/api/namespaces/Cuemon.Data.Xml.md new file mode 100644 index 000000000..5b8a93a95 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Data.Xml.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Data.Xml +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Data.md b/docfx/api/namespaces/Cuemon.Data.md new file mode 100644 index 000000000..86b654a7e --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Data.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Data +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Diagnostics.md b/docfx/api/namespaces/Cuemon.Diagnostics.md new file mode 100644 index 000000000..5b0abea8e --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Diagnostics.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Diagnostics +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md new file mode 100644 index 000000000..074c209f5 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Builder +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md new file mode 100644 index 000000000..6823df078 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Data.Integrity +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md new file mode 100644 index 000000000..524302326 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Http.Throttling +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md new file mode 100644 index 000000000..eb1c9052d --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Http +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md new file mode 100644 index 000000000..fbf645b09 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Mvc.Configuration +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md new file mode 100644 index 000000000..9176a52f9 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md new file mode 100644 index 000000000..3f93f281d --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md new file mode 100644 index 000000000..245683dbf --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md new file mode 100644 index 000000000..52e848713 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md new file mode 100644 index 000000000..61989495a --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md new file mode 100644 index 000000000..52c5a30d1 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md new file mode 100644 index 000000000..7dbfc3412 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Mvc.Rendering +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md new file mode 100644 index 000000000..af2cb92fd --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore.Mvc +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md new file mode 100644 index 000000000..940e51514 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.AspNetCore +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md b/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md new file mode 100644 index 000000000..83f02ceec --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Collections.Generic +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md b/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md new file mode 100644 index 000000000..1a916f2b9 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Collections.Specialized +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md b/docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md new file mode 100644 index 000000000..2bcf36552 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Data.Integrity +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Data.md b/docfx/api/namespaces/Cuemon.Extensions.Data.md new file mode 100644 index 000000000..df66f2a96 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Data.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Data +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md b/docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md new file mode 100644 index 000000000..e05dfd8c7 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.DependencyInjection +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md new file mode 100644 index 000000000..ca14535db --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Diagnostics +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Hosting.md b/docfx/api/namespaces/Cuemon.Extensions.Hosting.md new file mode 100644 index 000000000..5328ee946 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Hosting.md @@ -0,0 +1,7 @@ +--- +uid: Cuemon.Extensions.Hosting +summary: *content +--- +The Cuemon.Extensions.Hosting namespace contains extension methods and features related to the Microsoft.Extensions.Hosting namespace. + +Availability: NET Standard 2.0, NET Core 3.0 \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.IO.md b/docfx/api/namespaces/Cuemon.Extensions.IO.md new file mode 100644 index 000000000..e754b589c --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.IO.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.IO +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Net.Http.md b/docfx/api/namespaces/Cuemon.Extensions.Net.Http.md new file mode 100644 index 000000000..0efc16f9f --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Net.Http.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Net.Http +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Net.Security.md b/docfx/api/namespaces/Cuemon.Extensions.Net.Security.md new file mode 100644 index 000000000..92597b66a --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Net.Security.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Net.Security +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Net.md b/docfx/api/namespaces/Cuemon.Extensions.Net.md new file mode 100644 index 000000000..930022e57 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Net.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Net +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md new file mode 100644 index 000000000..6a0c49da5 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Newtonsoft.Json.Converters +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md new file mode 100644 index 000000000..1d8d95b1f --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Newtonsoft.Json.Diagnostics +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md new file mode 100644 index 000000000..fbc0de896 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Newtonsoft.Json.Formatters +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md new file mode 100644 index 000000000..83b0fce05 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Newtonsoft.Json +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Reflection.md b/docfx/api/namespaces/Cuemon.Extensions.Reflection.md new file mode 100644 index 000000000..4ed221717 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Reflection.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Reflection +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Text.md b/docfx/api/namespaces/Cuemon.Extensions.Text.md new file mode 100644 index 000000000..7cf0df37c --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Text.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Text +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md b/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md new file mode 100644 index 000000000..b59ed5748 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Threading.Tasks +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Threading.md b/docfx/api/namespaces/Cuemon.Extensions.Threading.md new file mode 100644 index 000000000..d0116856c --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Threading.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Threading +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md new file mode 100644 index 000000000..3a71857f1 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Xml.Linq +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md new file mode 100644 index 000000000..bb3791149 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Xml.Serialization.Converters +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md new file mode 100644 index 000000000..7423aaffc --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Xml.Serialization.Diagnostics +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md new file mode 100644 index 000000000..f95d50485 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Xml.Serialization +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.md new file mode 100644 index 000000000..bc715d299 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions.Xml +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md new file mode 100644 index 000000000..c7c7aee70 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md @@ -0,0 +1,7 @@ +--- +uid: Cuemon.Extensions.Xunit.Hosting +summary: *content +--- +The Cuemon.Extensions.Xunit.Hosting namespace contains types that provides a uniform way of doing unit testing that is used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. + +Availability: NET Standard 2.0, NET Core 3.0 \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.md new file mode 100644 index 000000000..2bd1476e5 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.md @@ -0,0 +1,7 @@ +--- +uid: Cuemon.Extensions.Xunit +summary: *content +--- +The Cuemon.Extensions.Xunit namespace contains types that provides a uniform way of doing unit testing. The namespace relates to the Xunit.Abstractions namespace. + +Availability: NET Standard 2.0 \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.md b/docfx/api/namespaces/Cuemon.Extensions.md new file mode 100644 index 000000000..755a5885d --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Extensions +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Globalization.md b/docfx/api/namespaces/Cuemon.Globalization.md new file mode 100644 index 000000000..58e69201d --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Globalization.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Globalization +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.IO.md b/docfx/api/namespaces/Cuemon.IO.md new file mode 100644 index 000000000..3f19e0001 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.IO.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.IO +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Messaging.md b/docfx/api/namespaces/Cuemon.Messaging.md new file mode 100644 index 000000000..5c896f629 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Messaging.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Messaging +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Net.Http.md b/docfx/api/namespaces/Cuemon.Net.Http.md new file mode 100644 index 000000000..c695f213d --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Net.Http.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Net.Http +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Net.Mail.md b/docfx/api/namespaces/Cuemon.Net.Mail.md new file mode 100644 index 000000000..8b19a206e --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Net.Mail.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Net.Mail +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Net.md b/docfx/api/namespaces/Cuemon.Net.md new file mode 100644 index 000000000..d032278b7 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Net.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Net +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Reflection.md b/docfx/api/namespaces/Cuemon.Reflection.md new file mode 100644 index 000000000..d5ca2c9d0 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Reflection.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Reflection +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Resilience.md b/docfx/api/namespaces/Cuemon.Resilience.md new file mode 100644 index 000000000..6cbae165d --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Resilience.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Resilience +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Runtime.Caching.md b/docfx/api/namespaces/Cuemon.Runtime.Caching.md new file mode 100644 index 000000000..ef3a5cee2 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Runtime.Caching.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Runtime.Caching +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md b/docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md new file mode 100644 index 000000000..989fdf49e --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Runtime.Serialization.Formatters +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Runtime.Serialization.md b/docfx/api/namespaces/Cuemon.Runtime.Serialization.md new file mode 100644 index 000000000..9f8aac99a --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Runtime.Serialization.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Runtime.Serialization +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Runtime.md b/docfx/api/namespaces/Cuemon.Runtime.md new file mode 100644 index 000000000..676c6c550 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Runtime.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Runtime +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Security.Cryptography.md b/docfx/api/namespaces/Cuemon.Security.Cryptography.md new file mode 100644 index 000000000..ef2e5f167 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Security.Cryptography.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Security.Cryptography +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Security.md b/docfx/api/namespaces/Cuemon.Security.md new file mode 100644 index 000000000..a0415171c --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Security.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Security +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Text.md b/docfx/api/namespaces/Cuemon.Text.md new file mode 100644 index 000000000..847d2a98b --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Text.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Text +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Threading.md b/docfx/api/namespaces/Cuemon.Threading.md new file mode 100644 index 000000000..86b0ec81e --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Threading.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Threading +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md b/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md new file mode 100644 index 000000000..40c272c78 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Xml.Serialization.Converters +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md b/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md new file mode 100644 index 000000000..74f4b9e11 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Xml.Serialization.Formatters +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.XPath.md b/docfx/api/namespaces/Cuemon.Xml.XPath.md new file mode 100644 index 000000000..03f396c5f --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Xml.XPath.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Xml.XPath +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.md b/docfx/api/namespaces/Cuemon.Xml.md new file mode 100644 index 000000000..bb2eb3695 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Xml.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon.Xml +summary: *content +--- +The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.md b/docfx/api/namespaces/Cuemon.md new file mode 100644 index 000000000..8d130fa46 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.md @@ -0,0 +1,5 @@ +--- +uid: Cuemon +summary: *content +--- +The Cuemon namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/docfx.json b/docfx/docfx.json new file mode 100644 index 000000000..0d4a83a71 --- /dev/null +++ b/docfx/docfx.json @@ -0,0 +1,859 @@ +{ + // "metadata": [ + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Core/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Data/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.data", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Data.Integrity/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.data.integrity", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Integrity/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.integrity", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Data.SqlClient/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.data.sqlclient", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Diagnostics/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.diagnostics", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.IO/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.io", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.1" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Net/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.net", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Resilience/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.resilience", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Runtime.Caching/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.runtime.caching", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Security.Cryptography/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.security.cryptography", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Threading/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.threading", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Xml/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.xml", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Collections.Generic/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.collections.generic", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Collections.Specialized/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.collections.specialized", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Core/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Data/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.data", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Data.Integrity/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.data.integrity", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.DependencyInjection/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.dependencyinjection", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Diagnostics/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.diagnostics", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.IO/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.io", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.1" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Net/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.net", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Newtonsoft.Json/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.newtonsoft.json", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Reflection/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.reflection", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Text/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.text", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Threading/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.threading", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Xml/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.xml", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netstandard2.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.AspNetCore/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/aspnet/cuemon.aspnetcore", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netcoreapp3.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.AspNetCore.Authentication/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/aspnet/cuemon.aspnetcore.authentication", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netcoreapp3.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.AspNetCore.Mvc/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/aspnet/cuemon.aspnetcore.mvc", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netcoreapp3.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.AspNetCore.Razor/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/aspnet/cuemon.aspnetcore.razor", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netcoreapp3.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.AspNetCore/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/aspnet/cuemon.extensions.aspnetcore", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netcoreapp3.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.AspNetCore.Mvc/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/aspnet/cuemon.extensions.aspnetcore.mvc", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netcoreapp3.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/aspnet/cuemon.extensions.aspnetcore.mvc.formatters.newtonsoft.json", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netcoreapp3.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/aspnet/cuemon.extensions.aspnetcore.mvc.formatters.xml", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netcoreapp3.0" + // } + // }, + // { + // "src": [ + // { + // "files": [ + // "Cuemon.Extensions.Xunit/**.cs*" + // ], + // "exclude": [ + // "**/bin/**", + // "**/obj/**" + // ], + // "src": "../src" + // } + // ], + // "dest": "api/dotnet/cuemon.extensions.xunit", + // "filter": "filterConfig.yml", + // "properties": { + // "TargetFramework": "netcoreapp3.0" + // } + // } + // ], + "metadata": [ + { + "src": [ + { + "files": [ + "Cuemon.Core/**.cs*", + "Cuemon.Data/**.cs*", + "Cuemon.Data.Integrity/**.cs*", + "Cuemon.Data.SqlClient/**.cs*", + "Cuemon.Diagnostics/**.cs*", + "Cuemon.IO/**.cs*", + "Cuemon.Net/**.cs*", + "Cuemon.Resilience/**.cs*", + "Cuemon.Runtime.Caching/**.cs*", + "Cuemon.Security.Cryptography/**.cs*", + "Cuemon.Threading/**.cs*", + "Cuemon.Xml/**.cs*" + ], + "exclude": [ + "**/bin/**", + "**/obj/**" + ], + "src": "../src" + } + ], + "dest": "api/dotnet", + "filter": "filterConfig.yml", + "properties": { + "TargetFramework": "netstandard2.1" + } + }, + { + "src": [ + { + "files": [ + "Cuemon.Extensions.Collections.Generic/**.cs*", + "Cuemon.Extensions.Collections.Specialized/**.cs*", + "Cuemon.Extensions.Core/**.cs*", + "Cuemon.Extensions.Data/**.cs*", + "Cuemon.Extensions.Data.Integrity/**.cs*", + "Cuemon.Extensions.DependencyInjection/**.cs*", + "Cuemon.Extensions.Diagnostics/**.cs*", + "Cuemon.Extensions.IO/**.cs*", + "Cuemon.Extensions.Net/**.cs*", + "Cuemon.Extensions.Newtonsoft.Json/**.cs*", + "Cuemon.Extensions.Reflection/**.cs*", + "Cuemon.Extensions.Text/**.cs*", + "Cuemon.Extensions.Threading/**.cs*", + "Cuemon.Extensions.Xml/**.cs*", + "Cuemon.Extensions.Xunit/**.cs*" + ], + "exclude": [ + "**/bin/**", + "**/obj/**" + ], + "src": "../src" + } + ], + "dest": "api/dotnet/ext", + "filter": "filterConfig.yml", + "properties": { + "TargetFramework": "netstandard2.1" + } + }, + { + "src": [ + { + "files": [ + "Cuemon.AspNetCore/**.cs*", + "Cuemon.AspNetCore.Authentication/**.cs*", + "Cuemon.AspNetCore.Mvc/**.cs*", + "Cuemon.AspNetCore.Razor/**.cs*" + ], + "exclude": [ + "**/bin/**", + "**/obj/**" + ], + "src": "../src" + } + ], + "dest": "api/aspnet", + "filter": "filterConfig.yml", + "properties": { + "TargetFramework": "netcoreapp3.0" + } + }, + { + "src": [ + { + "files": [ + "Cuemon.Extensions.AspNetCore/**.cs*", + "Cuemon.Extensions.AspNetCore.Mvc/**.cs*", + "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/**.cs*", + "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/**.cs*" + ], + "exclude": [ + "**/bin/**", + "**/obj/**" + ], + "src": "../src" + } + ], + "dest": "api/aspnet/ext", + "filter": "filterConfig.yml", + "properties": { + "TargetFramework": "netcoreapp3.0" + } + } + ], + "build": { + "content": [ + { + "files": [ + "api/**/*.yml", + "api/**/*.md", + "toc.yml", + "*.md" + ], + "exclude": [ + "bin/**", + "obj/**" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "globalMetadata": { + "_appTitle": "Cuemon", + "_appFooter": "Copyright 2008-2020 Geekle. All rights reserved. Code with passion; love your code; deliver with pride. 👨‍💻️🔥❤️🚀🤘
Generated by DocFX
", + "_appLogoPath": "images/50x50.png", + "_appFaviconPath": "images/favicon.ico", + "_enableSearch": false, + "_disableContribution": false, + "_gitContribute": { + "repo": "https://github.com/gimlichael/Cuemon", + "branch": "development" + }, + "_gitUrlPattern": "github" + }, + "dest": "wwwroot", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "template": [ + "default", + "templates/cuemon" + ], + "overwrite": [ + { + "files": [ + "api/namespaces/**.md" + ], + "exclude": [ + "obj/**", + "wwwroot/**" + ] + } + ], + "postProcessors": [], + "markdownEngineName": "markdig", + "noLangKeyword": false, + "keepFileLink": false, + "cleanupCacheHistory": false, + "disableGitFeatures": false, + "xrefService": [ + "https://xref.docs.microsoft.com/query?uid={uid}" ] + } +} \ No newline at end of file diff --git a/docfx/filterConfig.yml b/docfx/filterConfig.yml new file mode 100644 index 000000000..837050d4a --- /dev/null +++ b/docfx/filterConfig.yml @@ -0,0 +1,4 @@ +apiRules: +- exclude: + uidRegex: ^System\.Object + type: Type \ No newline at end of file diff --git a/docfx/images/32x32.png b/docfx/images/32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..b6ea41cf64ab51c8a39fa6f5658733a9f38900ed GIT binary patch literal 1123 zcmbVLO>EOv9QPCn3TmZIyFdlf!-%PyNbKj>kHkx;;@Dmzntr5_s5FLf?bpU??Pu&) zl4eC>lXj>yjYE%b>PMUj)m~tCWS-AVV3Rp_51z) z|M&k`8XNhd?_mEyj^p}DLwbdcApUyxv47NTzGTB;YE04zGEM7Nh`EA8rZ6aZ)(oy- z%UQU18|OGKG3{0->126WwTYLqVoa*x1&qycxxq$Y*|V5}DLmu)dH(tOuRL&_JbzXx zi{+q*Ywl1p#1qYts@-AJUog(3k0JGU_tRYEBhDb&WKD8Q2KRVu3&~apkZa`h)gILj;BJ(uQGf#J} z;05LKuCO0<0>wfmG^{{?DN*pe_+H!Sh*t1hH}*tF)r9~H6&#WI&}R9VKHep>*uCFS z3}oJ@qoK=^V$JEqp7*d%OM0GXCn?8u)Ph_rNT{e}kSrT%2n!n04FgHYR0?KM%62vO z;!1`nLPL}cS%VN7igrrVj7&yHB1A|jWV+mvA5qJ~^^Dy)~DlBh-kP@F(u;H>LAq#k`5r)TH3Iu6}S*fB%mf$jLJ?jH6tX`~mCC~2Z08w!M) zjG&&*7zI(6)7gy7ceu{~iJ8Eh5#r)_r&zjMtODcdZt1hdZu4NDbxg=w<6rajJ=Rvs zCB0B>Y)<9=`ugEuVySiOkDoqT*}7{FE=e!i?RM{QZ=%;c+R}cwMwjWv#hm;1FZTux z&9_z)wIBNqe6z8UyZ$(R_fO}@;;}2|Z+`fhV}AT-HDTWQV6nBi*?(i}=PjeX|EnJH z!MRJz&ldl?fB(#Q&-b7FKEA*ARmgpLd1PSas`=Z)(M0?8ldH(=|C|FYZoPJ5?VsLj1hfJyYTHBnBQ;% literal 0 HcmV?d00001 diff --git a/docfx/images/50x50.png b/docfx/images/50x50.png new file mode 100644 index 0000000000000000000000000000000000000000..093c47b1ad7862a6cdcace3539a4df7f817c847c GIT binary patch literal 283 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1Gdp%toLn>~)xuMI~Vj$3#*jAtt!+QMS)RyWiR}uy1FmKJ$ zKM-iZlcZ~(x$xws-+g(U{%J-(9o)>b%92BKRTq{&yDsyWZ`J(OmEV+ESvV#<)_PZ$ z5PR*Q^oeSJ_XY+=CKe6>1x;lypU77B-Xn3Ub|;r=F_!Q*#RtnVt<=kvd@K| z`(94HSQT{A#Ao{9g~fV5Gd6C8NMBE1yLo=P;myvAOiU~%zFmtl$*JU!^^9K4z|2ti XO!bnMz^^4hk1%+;`njxgN@xNA#n);X literal 0 HcmV?d00001 diff --git a/docfx/images/favicon.ico b/docfx/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..6edab735b36d700ab877a2374c30de7556428b41 GIT binary patch literal 360414 zcmeI5&9fZUd51^ADYA<d7b9$N!d|zn-3Z=9%^JQ~$79J^y#B)wy$< z8>D~M{;4OwwYv5%r&dp;$7$2_%=&zqt)5By3IFa~{wT5Z@#Q~VLe){xQI-a*5+sf(q%$c@*w14h9&m}%xO!btdjs8p*SxR+^tjyWk zJ^$V3vqT?l+lT!y24ni1Ym7--%Q;6K-;OfZsO#I)2X$y;`StVH6H7N6 zh5guZoA&hc=)WE%mLC54Ve-wbZ<2?%);Z&bIb)I6ZO{B^M{eUXcIo)%Q$3`~>&VJB zU!NY`N@YpUh4a2~khV|LPW#RH%;V9o*SdK8T#}Ght0Zl=PB+v1=Hn-6m6o|?`C~WN z$<~=C)AUT5Z2hsnbGGmVe}0{@wPrVcoUJ{1>rc?ts&G9D*C&pHb&547%vlS<@xo*H z-CE1SwZ$K&^I$B$&V=Qn^-tq(YhWn-V-J{R?I7oTVLb6R_vM=Qk7a!1&0E9m+R*Y3 zF+Oa=7`o2O=Q#d0L8}$MjI2Hn?ancE!u`mxKWx@p_&meea^V#V~<}pUePt=3gzd#zb9Z{?f3Yl`+TSGsg94#CTCHq zy3Z<--Oi)uBK^;Oe0V|+3`pTZ^rq6MCX3;uNpXJ(pmyOXz z-ms1J^X8d1vsC-pi@y&qC|jS_AG*ly6)&aoZ=OfD&M&3%r~M2e|M9&Q&yqXjCcpKE z{F%K|+4Js=8=bqnex~22{K(y>{jE=Lf62f89Hx=2H=n(1wUPa^HAMd3-}*293qDIN zh;4P&Nv(gwt^c+j8voogaSzobr>*s4&swop9oyEaLx0x4r9R`IKxN{ z%JxTwI`W2fXe;@r%Re>@ZCmGl&#J&ZO4$`p0QOu|Jl07&AadAoat|~|3ij4@`iP2JC^)5PTvU`LfPsZ({>E` z!)K0p$LzQo@{;dBzdBb!2l;7Sx$$b}j^X^IbE(*tG zIzs-s&*`y8k-hpp&v<<^8TJ>e{OA)sG|BPR(1*JmGd}aG%ES70?Avg?D5q}G^|JG^ z%U?EkyY~Ed*6+fb4zlCTyYc9xDyRQ0_72<9XWg;id*{96;a?se?}3u^k3P&kSATXj zx$xr)S+Z;X&(2fV&Qq=$o9omMW$1EDpNvtIQ}6tb&L^e=j*-J;f8~3xWV%8APw#wM z_^ga})=$xx;d#dFB5&B1KEtt!j;T*>a=_)`XNPO~Urz0xrh6aVJHAGJ^0~=>^WSe~ zeql13?3PR)Z~WwqES>woxnrL(9ZXez{KDJOWW0U-c4liYzpWF-fF)ZO@Dt1Jk7MhL ze#>&k41KuEF@2Ven|Fscye`KDE{AS~`KRBDoZaVSue|_|I#>BNN z*RuTTPp>9czV^;F6F)m=YpMAU`pnZc=cv=g-eFt%WQ@AGwX=AB@WR2CADf?_{r=f3 znQT6p{Qj8o6y--xq5e2-n$urdPM@I-yBxzKeYeT|oZGawHolLdGJSWEH*DKg{-U`K zalWbib`Kcd7xsP3oQ9A$?31xV8x|d#{MkF|2kY;p^1iyy%idWoyq3OGY46pVE}2s} z*CxmGQI+GXMe>H{ZGKhqdtKBnr~0&adZ!?NW$e=724-@o|v`g_b}?;zVX|4w*V=H2NBpZ_7#>15-5y@&;1`VG?AOSw4I{--2a95oNbP6uV%h%zMJRMP0o$x|C{^YHu8pTB){x0 z{}=y*tbevwv)_@kysxzRT{??f;ab+_*w#OjeURrLeaW2D&lKdhIS!HR*?Qe~%&L6C z`myJA<_CSHwQU|t{x2K6ue?f-PEiW6Apb@cDWck8Riv^KLTMY<)Jlnbqu+ z+wVl2vu;kg--$?nV>kR8f{(uY?31@p{2oHoyy`<7Sz?XKG~`2cg7lbt@kl>X** znp*!xh<(>)h?IVR$G*d3x_)r~554TU+A>c&i_SJVXJjCU-P+#?*zg}I?Ndv$YuX(yHf+5q03loTW22385ziBf2>=bqce0_NAI!JVa~`v zF8f#8of_y2UB+VDI`d%8$Ur^?``cXk-U#0#88c^WbcQbL=slJ?%o!QTW&iE}>pst~ zWdHT=0FU|^p?m+sd=M}OWz)~!1*O>=%Mh5b!@Q)4pvT2ik7#p3T%Q||G zr4DmO26EZ|E%b>1D;{xADKJ?pSt zr;hlq=3McA4{^R|EfD|WKlK6jDeS?E7#;FFV>1`l(mLyIEOnSOGLUQkR~;8*|2@P7 zt^Zp8wf^_!16u#9*ZHD7gw}tp|KA4n7XRQ6y+j1GC8v6%~NX`Ov{ zEOnSOGT3j~S=oML%~CXW-*9iXm;EpHwg%+cA0NOcV$NZ3NY0D;W^D9>E@NT4&ODei zGT3(-%>MSM&~&*PP86R2|dJMo3ZAA%wy%UfAh6^ zEMFL79Q-UE>Q83E+{V&F+i}L6XWKazwPWrlT92p1+=}Mj*LlXVJnWNe;dy@?*5SA+ z_kB?IZ8$e%&Uo=y{bVN0ZE*RYelEH2;|tmT+s5?cmCeDfS^RwIA74t&e*f(8Z^+Q5 zD)-}1_H8&fWX^aeT92p1+=h~${y)9*X<~ntbB;W_kp9E5p9^~@Yxmwe?;Y2%a@C)& zZ++4~b1{G9oFB9AGGoMJ^^=(}x4vbjKl*?8mxr0TEax0KcI240*dAMZdvfmUqpy>D zAKhE0J87OKv#+JAKf8K7mi0;hvj6z@|M~f!XEw1MJCNtu->#YcUp@0Gmvi~QcI8@D z&+5{j&+#R1fAbOhyYRC3M^|1?LzEfEa{A{S2Ikmuv*(M?zL*%_{N?d4`=7Afri9M1 z$xu1tSk4%n!@eA|?!W(w_p?jHjy(JObM#x3)4%vX@f37Yq_ZKOaPrL}7prbK!#`gBN_1yj}_Tvv__NT4Sy`Ok_7*F<( z)Bbj^XKVi9XWPFOUuJ*B{}WrbDZzy2WUMk{!>-xg?ElL5UdhdH{39(}gA(r_)ATOB!us>2*C&l@A}xO}$HQ5%eb3wzF~@!6bN3f?+TKdTwT!|3rk^?c zz>xOOz6*AL{X0NY{a%y5=bNJ+3}t`52RK!``+1`y*}wi;Z@>9tI=p^xeVxvy`Bc(o z&iH`rU*GQ3M0c|P_I^9IH3Z+rZhht0|Hk+n)>kh3_h)zGyic3^zGVNLxD?s?Z}u`f!PF3W+u!K)?H+ud{?_N_ zw||(U*8gtn0IZ_Vp>l7>PUX$-|1eM4zhC=5y7efr^zc^t4thk1d71CnZ~n^u-R#cZ zKI9)g$7Fk(3-e;_ko||TKX(7-);CE^iFwKX{n)*TfAk!a?QJg1OZJ!j%?Gsp_u~g) z{D(8~pL5^d*Yo9w?*C=~0qtDmFS38*y(R0C;(tHuzV?56;GXqB{EPq82iU_BgJE*a zoUxe;>#X*F1FjF^U;K;zMn1s0r2U`ve|rAYpARVh4;bg!?}xeKf1|Zu&;R@5J-oLN z|Ki_#K=*$m`hnJet^d0JYvcpE{~Klxp!h%Hy3c)p*8fKA&wc;TcO+nO%$%{c{~K`~ z2>8baW7?gu7+dz=pZ)v#PF$CMc&^SqOP~J@=nnz^UF_CZTiJg=d$aE&m;JT>OMQTt z+ZV%K`Vs%)-+Vyve*{0!`mgn0>whC3U|k|k5j*Gb{qHVuyRWwB6+P?zZ@@U9^}iAO zbASHZ7sFlp5&r}7&V4_*_!s}$|26Uf_9d)S+W&P~?RVRvSM;p?-+=Kz>whEmOZR^V zshuosbUKNAGiTPYT^h$f1ONQa66-)LIrCzk=x*x2|IF_hj&Jep5wqV`OrQUv=UD8{ zJeXH#fBayIIsb>jwk7s+#9YvCtoan}oHu9my&r4mk7{$W-&Ty(_BJncnMv?O`d=9S zuao6()8j`vKGr~}kK+p}jIDul!GKA!eP-a}Q(OI;U-<{9LG2Xkzu;!qbzWi7H#IcUZ4%>FX z&tqUNra7P&`OCJy#G;RnFMXU?x_kMqQd1pVN_Cbdvt{;tINj&Q#9j<@G5+!OH_yD8 zSbFQsTS_D80G(rlFc<$F);;lGxx|S7{jPOY{1f*-x$;S()KCYP)B0&r{4ZVxJpaV~ zi)St-O5^ANoriIsWAWdRdB0AI|6=erSH%A~oENR9;=cpmp7p%4_!s|??`6cl_^*U? zzzT~0ivP2HuAul|xpIhq@h|=>F_~=z@h|?x|7`KD6iob!fAL?5$!sf#fAKH=XNz~G zVB%l=i~mYYW?Mo0i+}MyTf8d;6aV60{8wT!+X~`e{EPqD;$112_!s};zY>$#RuKQ< zU;NJ&?@GbMzxWsbm6*)7g7_Ez;(xYyR|+Qn#lQHk#ALP=#J~6#|FgxrQZVr^{>6VK zCbO*|{>8uepDo^%f{B0eFa9ernQaB}FaE{oi+819 z;$QrW|4K||TS5GbfAK$CyekD0|KeZ#S7I{T3gTb?U_IiUydYxv^6nOM^J=-k`Em&L#MA7Y&n z|8d;oi2n|JSKT`({ul0X#J~8T{WAseFa9HcmZ|u!_&>z{zN$~ff5m^r|K&ad6#wF1 z{EL78teL&&lAgcsTUK=L?cmFL{@>yLK==Q;|JVKhaPQYtJ%8W#b@4C$#lQI9y!&hW zD(-u$o{!D_I(qPXP4=Su-V-lre01*Z;LF>}d?*HT1Qz-r`{tvOg zuj*6$i+?@;)AK+7tch4O#C@*%%wKQNxwnHa>-k@Y{ekZPclQ143l#tJc^rG468|0e z*7LtYzTvzm7ysg4`+x2K{a%w;G6xxwnHaEB-6~7u~~mIj{Jy_^7A-j*k1abo_jd4Q#ec>Bq8Q%gyyCy&zv92*zmGMVtAMHivNoLivNoL zKGqP6#*TqY9uJ*+JNUBVzv6$FIA7FO@n7*@@n7-Z#~NbMlH>8%vZHfv2VYkFSNt!E z;a$!v{ww|~{wx0bSVJrtI|eR!Jaq2u;LD2tivL~Wd{JA)f5m^rf5m?vYluZlj>lum zj?TRud|B~d@xLgBcR8>4ulTR{ulVm{4Y6qK7`Wu|(7CsRFDw2l{&$J66WGSt?K0gSxu7l-5m?Ur+J-A}*G49zA&75Q~-^kH?lBoqId@vav7z8!+$J z2Jx@o|DE{%|LNBPl@GUKc^iEY_m&dpi{wNPUWde@v18zp$3y4d4u_vVNRHF}w7kdU z=QW2ieqY4JQqH3XZ&Q46$vloNJ39AvIQsJFgnz#l?4j)EHTClCU;QS*@0Y@Pk(}tk z+Z10Mn}H>dht9nnRuTQ3e)@D`34fV-Uc|%F&SOXKclhFxc^q4IbnfjC5&kRjKhJjr z#DBb>|HZ^VezLUqICpumtB*UxqOoJ(lE*{m-VWk_o-sG3HAVa@{?}P&@ROK)S99sU zK4MoNcZfwxj>lumj?TRu>To$$9sEShb!RRn9b#ASclhGi3@mv(bnfji7Ta~k!B1lH zUCpKYT7+G_-{Ff(=5cJ<(Yd!n9WKYJgP(}G?##ucL+tAP4qqIbfhCWJ&b=MRV!O^b z_(@E@tGRSvi?FNrJA84;JdQ0pI`?*{!{u0Y@DnlDow=BFh+Vzky?N%%#1cNK{H?)t zfzG`h#$vn9IQU6SzN@)(UyHD-_d9%1+z-z^I!5Q-4t2O3s}6o5=DIT%lMb<~_dAul zf4{U39gf9zopJD!n0!}r>An_W*FOAJeRN+pj=%4le#fs4mt)nzPsChz=3>$zcKx6K z`0viYsW2t}J!dhN|CE0BcPzH+jDw%VYQi*zYuf%F&C3A zvG3e-@qfZ}lM;Rrlh0}{^IVIDv@3qx#P?NqwL))0+D3hg=b?Q7KNi!?G=ScQwvYPP z8Ze{};K$9pU$xQOkhW3ZVt;5Kz>h_AGYz1(q3xsowFV671Nd<>?^kW~Hl%ITx7Z)r z2k>JN-An`MZD{+bf2{#S`T&01%==Xvy$xxbG5bay@-fG@vGz6Y*mA};9(oti%`|}C zVzXzP`5;S}SM}Ajeo=qEpE`AX{ifxj{=@Tm{f2esaEzSIykE7^+thu)JeLcz?^+Ie6*WmvVVQcIsK)ln?8} z*tfOk>!Yu;uJZcG8_SQ|q%8h-pQWN-5#3A!=xq-C8+Uns76bF-&|09cx#XY<;t2_cZhu>VjjgUl9L-yhH=| z34Sxh92s6c^J?<&FAqJ6iLD97sMQH&qn&S%$8Olpj;*crn>S`&A9-Whxa=ReUN_I1 zlRrm4;=jcCb!j%=|1*DwM`PaNq{U_U@@!Cskv9C(Z}_~?`V#-U_vh$*s2p1g>1G;0 zZ$sH-%zlw2%!}qgy{7!be$n&BCj2vpqB*ubPrr))&3IT^ZTzIIpT*V=dC1G=N1Z18 zTN~s5;;R>Zv#qlDSNxyKIsyY_oXqtavU_;**hPNWx6k^*=L}VSdeM`ak=gkL?HST2&v+DX*UOdF{%zEZJV7D82EMH;%`lzp9+^6#ttu zv7{FGNtHj%@j9}5wteh2zF+y?D?6X{+4p=s`__;D^L?PVys>A@Lt5oQx`0EmKWcTdBKfmR}HGALB z-}9rNqCPlZR?qroJ)wS4PCs>X`qBP>D01e}FMd+jujW(_*e6rcIfnh- zDA&r`+qE*eW4ned`($Z~W23j&>^a7KkUiwz?)mdR>&u%{ULWFL@qa370sfok+jBpM z?4EzS=jS`UcD+gmd3`kD-;Z6lY~LBMal_vssoTCzJ?k6Us&o*;b>=!1?qZoIdW*q^ zW6uHEJrCjgy;XC{>m!eyyn24zCS~!zyT3rcv2Zn&d7!r{KcC}uWQRTfo!+|ghJ9FH z;(wRVy83Ke4?9eSyIAIl-rDMY%I%Qdcs2Ix-t&8zeIM43C3|0p^VH>-I(CoHG|~3m zpT3**Z{u*TXYO zySBR6M>}Mi3U{&06TR)jX0eQk4lHjwC*Ma|KViML$2QNlZR@sctBZZKL#9}`n#w%T z+dgcjF~`48o~zFBV0FEn|(rk7MC#D)T^Z;$#_|%+=2L@l?2rWuEA5 zu6`l8ma(qk$FXoVm3g2yak30f=4xmBcq-h*GEek2SHF;4%UIX&<5;+w$~@4UI9UcK zbG0*mJQeO@nJ0Rit6xa2WvpxXaV%U-Wgd0E3yyy0nroey_Bw%IPKCQz<|+Q4%ig0C z|KfivCXc>2N)A7N5Jmdp{$aB8bo)PlbNnu?)`4lR15=9sp8ch>7mNJ(kN6k=L+=A( z@p;YTWbY&Xr^!9*HTD~t-9GyF++9Doo@D8Knk$W?gN@F;9q=FgN%^$a1MJrapF{WI z{Z9PvVciq|t-t3S>$*5q{%@SWk!0z$G*=o&2OFLHbr}DN)xR~b^8E<*8#;FP(ZBaQ z@xO<4Py8$X_W^RtKE(eXnD=`t@h|>cvfD>9@h|?hpD*9fH`R~!|9fW2$Jf})ivNoL zeFXBBeJK9#d94!v;=d)keKZsQ;=k$sR`tv87qtK1GgGne`ThJ9|10A^JWVC%du)Bg z=b`&h{NM9hCH}?#(2$q(FaE`UtowQ5{q0}%Hr z_z(X+Mop=Fct8DKeT>O{-{0#~{EPpX_5ykQN6Ax+jF-&GGw;jdU;M|+zp?-5)}t&v zy!9|qYNi8p#~SL{x3c&b|Kh(HQ&U=l#Qz@n_Mi3Tm39AL`~KhJ7QB6P>zhQWnGVpM z;(tWzfcO{xBdulPKO*kg3y6R5FaF2*tqquqDHs3ZU;K-I_uJ=u?iJIsxP8n8J@A}b z_y5iA_rvES+W%|+zYmVvjw$}T@h<)&Bepq5pJ*8ayi+}Mi{`LL;h}Ho; z|KEA;#~P*k|9;mh#s7%7=X(I+U;OLyKYjkE&;NGT0mXmCe?9-#^M5`6?`Iv*{$Kll z?yaYgEB^PxJ)cpCfAK#B?wOLF|JwievkqwgAK^Yg@Bb9;0~G)J;a>ay2)Wn(zmR+G1H`}h*ZyDo zf9?PKSqHTLk8mHL{eR&;K=HpH?zR7qkbCX_3%S?+zhCafzxao#Ddmd){jLMr|3|nE zX#Zch4-o(2U(f&a{7=vS`dJ4Q{}unW|L=z-?y0Agi+}Mi{>8t3|1Y9-K==Q;|JUz- z>G!|%`(HcjfS&*LyI0ZvKccv=&;NIL5ARLHzxdble?9-#^Z$O<0mXmC|1ieJe60B2 z5BGdVA^ydG%)Ezl6aV6WiTnT4PoGXK;r~m`(S!E?{j39u|I>*7`2AA3M{e{W{>A@n z_W{A@|A(JHNLKpW(tzoNcUFjD+|w$rzc!#dbji9(eq-Z{vt^CX++xbb{}$_c2jagz z<7Mli_>X`8ulT>a0*HU{KifTt;{Pu2!5Q%{{>6U)#expSzxWsb3j#h6Rs4&8@gG33 zphNL5{>A@-fDc3!|KeZ#2T&~NQ2dL3@xLJ815w4l_!s{H6bm{O|KeZ#F9`TRRPitV z#eV?Bf)2&M_!s{R0zME`{EL6_A3(97L-8;E#s7kU4@4FJ;$Qp+P%P+B{EL6_zaZcP zQN_Rb7ykhi3py14;$QqP2>3u$@h|?xe*ndT4#mIt7yki+}MSK(U}h@h|?x z|AK%IL>2$yU;GD9Ea*`Di+}OIAm9U0#lQF${{a*WIu!rnU;Hl!_&`+gFaE`U0L6k1 z#lQF${|f>>5LNt(fAJqcv7kfoFaE{Y76cjCW3 z<7Mk%76164^243AaSWYgey;exvjCtMvjMS7`8tNT(Zd+NtTCFeA^7| zf%-Rxx-R`>8jyeF0wO-*<|Ef=Iw=iE1JZyrFjEa!O!}-uHeH{pevbEB1@_hT&3;V! z$>m@EA^%uwAU{6HKja_X{Udkj$xg2QOV^7u5W9Yg=B@M6fHWWtNCVQqwgw(&Lv?QZ YJV&~|QIPb5OKj!x5BW!?fgbtze-mLZ0ssI2 literal 0 HcmV?d00001 diff --git a/docfx/index.md b/docfx/index.md new file mode 100644 index 000000000..beeb7c169 --- /dev/null +++ b/docfx/index.md @@ -0,0 +1,518 @@ +--- +title: Welcome to Cuemon .NET Standard at Github.IO +documentType: index +--- +
+
+
+
+
+
+

Cuemon .NET Standard 5.0.2018.250

+

Cuemon .NET Standard is an open-source family of .NET Standard assemblies that, by heart, is free, flexible and built to extend and boost your agile codebelt.

+
+
+ + + +
+
+
+
+
+
+
+
+

NuGet Packages - One Size Fits All

+
+
+
+ +
+ + +
+
+ + Cuemon.Collections.Specialized
+ Cuemon.Core
+ Cuemon.Data
+ Cuemon.Data.XmlClient
+ Cuemon.Integrity
+ Cuemon.IO
+ Cuemon.Net
+ Cuemon.Reflection
+ Cuemon.Runtime
+ Cuemon.Runtime.Caching
+ Cuemon.Security
+ Cuemon.Serialization
+ Cuemon.Serialization.Xml
+ Cuemon.Threading
+ Cuemon.Web
+ Cuemon.Xml +
+
+
+ +
+ + +
+
+ + Cuemon.AspNetCore
+ Cuemon.AspNetCore.Authentication
+ Cuemon.AspNetCore.Mvc
+ Cuemon.AspNetCore.Mvc.Formatters.Json
+ Cuemon.AspNetCore.Mvc.Formatters.Xml
+ Cuemon.AspNetCore.Razor.TagHelpers
+ Cuemon.Core
+ Cuemon.Integrity +
+
+
+
+
+
+
+
+
+
+

NuGet Packages - When Size Matters

+
+
+ +
+ + +
+
+ + Cuemon.Core
+ Cuemon.Integrity +
+
+
+ +
+ + +
+
+ + Cuemon.AspNetCore
+ Cuemon.Core +
+
+
+ +
+ + +
+
+ + Cuemon.AspNetCore
+ Cuemon.Core
+ Cuemon.Integrity
+ Cuemon.Serialization.Json
+ Cuemon.Serialization.Xml +
+
+
+
+
+ +
+
+
+
+ +
+ + +
+
+ + Cuemon.AspNetCore
+ Cuemon.Core
+ Cuemon.Serialization
+ Cuemon.Serialization.Json +
+
+
+ +
+ + +
+
+ + Cuemon.AspNetCore
+ Cuemon.Core
+ Cuemon.Serialization
+ Cuemon.Serialization.Xml
+ Cuemon.Xml +
+
+
+ +
+ + +
+
+ + Cuemon.AspNetCore.Mvc
+ Cuemon.Core +
+
+
+
+
+
+
+
+
+ +
+ + +
+
+ + Cuemon.Core + +
+
+ +
+ + +
+
+ + / + +
+
+ +
+ + +
+
+ + Cuemon.Collections.Specialized
+ Cuemon.Core
+ Cuemon.Runtime +
+
+
+
+
+
+
+
+
+ +
+ + +
+
+ + Cuemon.Core
+ Cuemon.Data
+ Cuemon.Xml +
+
+
+ +
+ + +
+
+ + Cuemon.Core
+ Cuemon.IO
+ Cuemon.Reflection
+ Cuemon.Security +
+
+
+ +
+ + +
+
+ + Cuemon.Core + +
+
+
+
+
+
+
+
+ +
+ + +
+
+ + Cuemon.Core
+ Cuemon.Runtime
+ Cuemon.Security +
+
+
+ +
+ + +
+
+ + Cuemon.Core + +
+
+ +
+ + +
+
+ + Cuemon.Core + +
+
+
+
+
+
+
+
+ +
+ + +
+
+ + Cuemon.Core
+ Cuemon.Reflection +
+
+
+ +
+ + +
+
+ + Cuemon.Core
+ Cuemon.Runtime +
+
+
+ +
+ + +
+
+ + Cuemon.Core
+ Cuemon.IO
+ Cuemon.Runtime +
+
+
+
+
+
+
+
+
+ +
+ + +
+
+ + Cuemon.Core + +
+
+ +
+ + +
+
+ + Cuemon.Core
+ Cuemon.IO
+ Cuemon.Serialization +
+
+
+ +
+ + +
+
+ + Cuemon.Core
+ Cuemon.Serialization
+ Cuemon.Xml +
+
+
+
+
+
+
+
+
+ +
+ + +
+
+ + Cuemon.Core + +
+
+ +
+ + +
+
+ + Cuemon.Collections.Specialized
+ Cuemon.Core
+ Cuemon.Integrity +
+
+
+ +
+ + +
+
+ + Cuemon.Core
+ Cuemon.IO
+ Cuemon.Runtime
+ Cuemon.Runtime.Caching
+ Cuemon.Security +
+
+
+
+
+
+
\ No newline at end of file diff --git a/docfx/templates/cuemon/index.html.tmpl b/docfx/templates/cuemon/index.html.tmpl new file mode 100644 index 000000000..64dd4db04 --- /dev/null +++ b/docfx/templates/cuemon/index.html.tmpl @@ -0,0 +1,17 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} +{{!include(/^styles/.*/)}} +{{!include(/^fonts/.*/)}} +{{!include(favicon.ico)}} +{{!include(logo.svg)}} + + + + {{>partials/head}} + +
+ {{{conceptual}}} + {{>partials/footer}} +
+ {{>partials/scripts}} + + \ No newline at end of file diff --git a/docfx/templates/cuemon/layout/_master.tmpl b/docfx/templates/cuemon/layout/_master.tmpl new file mode 100644 index 000000000..0c151594e --- /dev/null +++ b/docfx/templates/cuemon/layout/_master.tmpl @@ -0,0 +1,65 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} +{{!include(/^styles/.*/)}} +{{!include(/^fonts/.*/)}} +{{!include(favicon.ico)}} +{{!include(logo.svg)}} +{{!include(search-stopwords.json)}} + + + + {{>partials/head}} + +
+
+ {{^_disableNavbar}} + {{>partials/navbar}} + {{/_disableNavbar}} + {{^_disableBreadcrumb}} + {{>partials/breadcrumb}} + {{/_disableBreadcrumb}} +
+ {{#_enableSearch}} +
+ {{>partials/searchResults}} +
+ {{/_enableSearch}} +
+ {{^_disableToc}} + {{>partials/toc}} +
+ {{/_disableToc}} + {{#_disableToc}} +
+ {{/_disableToc}} + {{#_disableAffix}} +
+ {{/_disableAffix}} + {{^_disableAffix}} +
+ {{/_disableAffix}} +
+ {{!body}} +
+ + +
+
+ {{^_disableAffix}} + {{>partials/affix}} + {{/_disableAffix}} +
+
+ {{^_disableFooter}} + {{>partials/footer}} + {{/_disableFooter}} +
+ {{>partials/scripts}} + + \ No newline at end of file diff --git a/docfx/templates/cuemon/partials/head.tmpl.partial b/docfx/templates/cuemon/partials/head.tmpl.partial new file mode 100644 index 000000000..f66a69b23 --- /dev/null +++ b/docfx/templates/cuemon/partials/head.tmpl.partial @@ -0,0 +1,28 @@ + + + + + + + {{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}} + + + + {{#_description}}{{/_description}} + + + + + + + + {{#_noindex}}{{/_noindex}} + {{#_enableSearch}}{{/_enableSearch}} + {{#_enableNewTab}}{{/_enableNewTab}} + \ No newline at end of file diff --git a/docfx/templates/cuemon/styles/main.css b/docfx/templates/cuemon/styles/main.css new file mode 100644 index 000000000..a42dcd26b --- /dev/null +++ b/docfx/templates/cuemon/styles/main.css @@ -0,0 +1,60 @@ +nav .container { + margin: 5px auto; +} + +nav .container .navbar-header { + padding-right: 25px; +} + +.hero { + height: 400px; + font-weight: 300; + text-align: center; + background-image: url("https://nblcdn.net/cuemon/i/splash.jpg"); + background-position: center; + background-size: cover; +} + +.caption.header.cuemon { + color: #000; + position: absolute; + padding: 0 10px 7px; + background: rgba(0,0,0,0.5); +} + +.wrap { + position: relative; + min-height: 400px; +} + +.bottom { + position: absolute; + bottom: 5px; + width: 100%; +} + +.btn { + margin: 5px 0; +} + + .btn a { + color: white; + } + +#wrapper .container.nuget { + padding-top: 25px; +} + +.btn-omen { + background-color: #A177B0; + color: #fff; +} + +.btn-omen-package { + background-color: #7C528B; + color: #fff; +} + +.container.nuget .row button { + margin-top: 30px; +} diff --git a/docfx/toc.yml b/docfx/toc.yml new file mode 100644 index 000000000..e80abea64 --- /dev/null +++ b/docfx/toc.yml @@ -0,0 +1,15 @@ +- name: Cuemon Conventions + href: api + topicHref: api/index.md +- name: Core API + href: api/dotnet + topicHref: api/dotnet/index.md +- name: Extensions for Core API + href: api/dotnet/ext + topicHref: api/dotnet/ext/index.md +- name: ASP.NET Core API + href: api/aspnet + topicHref: api/aspnet/index.md +- name: Extensions for ASP.NET Core API + href: api/aspnet/ext + topicHref: api/aspnet/ext/index.md \ No newline at end of file From 90164031bc46e36b4c56719503458d64f994fe11 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 16:52:12 +0200 Subject: [PATCH 130/385] Updated tags. --- .../Cuemon.Extensions.Xunit.Hosting.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj index 538f3b388..d18933038 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj @@ -9,7 +9,7 @@ Cuemon.Extensions.Xunit.Hosting Cuemon.Extensions.Xunit.Hosting The Cuemon.Extensions.Xunit.Hosting namespace contains types that provides a uniform way of doing unit testing that is used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. - host-test + host-test class-fixture host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services From f6e036c7f4d12257402283612bf9f82414b3782f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 19:26:57 +0200 Subject: [PATCH 131/385] Fixed test --- .../Reflection/AssemblyDecoratorExtensionsTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs index 30d13fbf3..9d6fcffc8 100644 --- a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs @@ -36,7 +36,7 @@ public void GetTypes_ShouldReturnAllTypesFromCuemonCore() var disposableTypesCount = Decorator.Enclose(disposableTypes).Inner.Count(); var configurationTypesCount = Decorator.Enclose(configurationTypes).Inner.Count(); - Assert.InRange(allTypesCount, 465, 470); // range because of tooling on CI adding dynamic types + Assert.InRange(allTypesCount, 455, 475); // range because of tooling on CI adding dynamic types and high range of refactoring Assert.Equal(4, disposableTypesCount); Assert.Equal(2, configurationTypesCount); } From 66266050e2bf141330f826f0a65e9e25ba3cfe06 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 21:14:12 +0200 Subject: [PATCH 132/385] Fixed error in project file. --- .../Cuemon.Extensions.Xunit.Hosting.csproj | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj index d18933038..a431f7ef8 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj @@ -12,6 +12,14 @@ host-test class-fixture host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services + + + + + + + + @@ -26,6 +34,5 @@ - \ No newline at end of file From 2fc7d1bd0e84d1642ea959dbc03564c757cbf976 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 21:32:07 +0200 Subject: [PATCH 133/385] Quality gate; justifications for many generic arguments. --- src/Cuemon.AspNetCore/GlobalSuppressions.cs | 17 ++- src/Cuemon.Core/GlobalSuppressions.cs | 135 ++++++++++++++++++++ 2 files changed, 148 insertions(+), 4 deletions(-) diff --git a/src/Cuemon.AspNetCore/GlobalSuppressions.cs b/src/Cuemon.AspNetCore/GlobalSuppressions.cs index 01f8ad206..23a31c393 100644 --- a/src/Cuemon.AspNetCore/GlobalSuppressions.cs +++ b/src/Cuemon.AspNetCore/GlobalSuppressions.cs @@ -3,7 +3,16 @@ // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3442:\"abstract\" classes should not have \"public\" constructors", Justification = "Infrastructure class.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Infrastructure.ConfigurableMiddlewareCore`1.#ctor(Microsoft.AspNetCore.Http.RequestDelegate,System.Action{`0})")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3442:\"abstract\" classes should not have \"public\" constructors", Justification = "Infrastructure class.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Infrastructure.MiddlewareCore.#ctor(Microsoft.AspNetCore.Http.RequestDelegate)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3442:\"abstract\" classes should not have \"public\" constructors", Justification = "Infrastructure class.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Infrastructure.ConfigurableMiddlewareCore`1.#ctor(Microsoft.AspNetCore.Http.RequestDelegate,Microsoft.Extensions.Options.IOptions{`0})")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "Generic parameter; implementation reflects usage.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Hosting.HostingEnvironmentMiddleware.InvokeAsync(Microsoft.AspNetCore.Http.HttpContext,Microsoft.Extensions.Hosting.IHostEnvironment)~System.Threading.Tasks.Task")] +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Major Code Smell", "S3442:\"abstract\" classes should not have \"public\" constructors", Justification = "Infrastructure class.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Infrastructure.ConfigurableMiddlewareCore`1.#ctor(Microsoft.AspNetCore.Http.RequestDelegate,System.Action{`0})")] +[assembly: SuppressMessage("Major Code Smell", "S3442:\"abstract\" classes should not have \"public\" constructors", Justification = "Infrastructure class.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Infrastructure.MiddlewareCore.#ctor(Microsoft.AspNetCore.Http.RequestDelegate)")] +[assembly: SuppressMessage("Major Code Smell", "S3442:\"abstract\" classes should not have \"public\" constructors", Justification = "Infrastructure class.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Infrastructure.ConfigurableMiddlewareCore`1.#ctor(Microsoft.AspNetCore.Http.RequestDelegate,Microsoft.Extensions.Options.IOptions{`0})")] +[assembly: SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "Generic parameter; implementation reflects usage.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Hosting.HostingEnvironmentMiddleware.InvokeAsync(Microsoft.AspNetCore.Http.HttpContext,Microsoft.Extensions.Hosting.IHostEnvironment)~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up to a max. of 5 generic parameters.", Scope = "type", Target = "~T:Cuemon.AspNetCore.ConfigurableMiddleware`3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up to a max. of 5 generic parameters.", Scope = "type", Target = "~T:Cuemon.AspNetCore.ConfigurableMiddleware`4")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up to a max. of 5 generic parameters.", Scope = "type", Target = "~T:Cuemon.AspNetCore.ConfigurableMiddleware`5")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up to a max. of 5 generic parameters.", Scope = "type", Target = "~T:Cuemon.AspNetCore.ConfigurableMiddleware`6")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up to a max. of 5 generic parameters.", Scope = "type", Target = "~T:Cuemon.AspNetCore.Middleware`3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up to a max. of 5 generic parameters.", Scope = "type", Target = "~T:Cuemon.AspNetCore.Middleware`4")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up to a max. of 5 generic parameters.", Scope = "type", Target = "~T:Cuemon.AspNetCore.Middleware`5")] diff --git a/src/Cuemon.Core/GlobalSuppressions.cs b/src/Cuemon.Core/GlobalSuppressions.cs index 801679851..90e17d5f1 100644 --- a/src/Cuemon.Core/GlobalSuppressions.cs +++ b/src/Cuemon.Core/GlobalSuppressions.cs @@ -112,3 +112,138 @@ [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateSix``6(``0,``1,``2,``3,``4,``5)~Cuemon.Template{``0,``1,``2,``3,``4,``5}")] [assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateTen``10(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9}")] [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic.", Scope = "member", Target = "~M:Cuemon.Template.CreateTen``10(``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``10(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``11(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``12(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``13(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``14(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``15(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``16(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``7(System.Action{``0,``1,``2,``3,``4,``5,``6},``0,``1,``2,``3,``4,``5,``6)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``8(System.Action{``0,``1,``2,``3,``4,``5,``6,``7},``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``9(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``10(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``11(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``12(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``13(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``14(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``15(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``16(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``4(System.Action{``0,``1,``2,``3},``0,``1,``2,``3)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``5(System.Action{``0,``1,``2,``3,``4},``0,``1,``2,``3,``4)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``6(System.Action{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,``5)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``7(System.Action{``0,``1,``2,``3,``4,``5,``6},``0,``1,``2,``3,``4,``5,``6)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``8(System.Action{``0,``1,``2,``3,``4,``5,``6,``7},``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Action delegates.", Scope = "member", Target = "~M:Cuemon.ActionFactory.Create``9(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.ActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8},``9}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8},``9}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``11(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``10}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``11(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``10}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``12(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``11}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``12(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``11}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``13(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``12}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``13(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``12}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``14(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``13}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``14(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``13}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``15(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``14}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``15(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``14}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``16(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``15}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``16(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``15}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``17(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15},``16}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``17(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15},``16}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``4(System.Func{``0,``1,``2,``3},``0,``1,``2)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2},``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``5(System.Func{``0,``1,``2,``3,``4},``0,``1,``2,``3)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3},``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``6(System.Func{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4},``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``7(System.Func{``0,``1,``2,``3,``4,``5,``6},``0,``1,``2,``3,``4,``5)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5},``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``8(System.Func{``0,``1,``2,``3,``4,``5,``6,``7},``0,``1,``2,``3,``4,``5,``6)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``8(System.Func{``0,``1,``2,``3,``4,``5,``6,``7},``0,``1,``2,``3,``4,``5,``6)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7},``8}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Func delegates.", Scope = "member", Target = "~M:Cuemon.FuncFactory.Create``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.FuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7},``8}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``11(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``11(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``12(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``12(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``13(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``13(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``14(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``14(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``15(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``15(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``4(System.Func{``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``5(System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``6(System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``7(System.Func{``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``7(System.Func{``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``8(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``8(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskActionFactory.Create``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.TaskActionFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Threading.Tasks.Task{``9}},``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8},``9}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``11(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Threading.Tasks.Task{``10}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``10}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``12(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,System.Threading.CancellationToken,System.Threading.Tasks.Task{``11}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``11}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``13(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,System.Threading.CancellationToken,System.Threading.Tasks.Task{``12}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``12}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``14(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,System.Threading.CancellationToken,System.Threading.Tasks.Task{``13}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``13}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``15(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,System.Threading.CancellationToken,System.Threading.Tasks.Task{``14}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``14}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``16(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,System.Threading.CancellationToken,System.Threading.Tasks.Task{``15}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``15}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``8(System.Func{``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task{``7}},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Threading.Tasks.Task{``8}},``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7},``8}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Threading.Tasks.Task{``9}},``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8},``9}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``11(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Threading.Tasks.Task{``10}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``10}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``12(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,System.Threading.CancellationToken,System.Threading.Tasks.Task{``11}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``11}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``13(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,System.Threading.CancellationToken,System.Threading.Tasks.Task{``12}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``12}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``14(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,System.Threading.CancellationToken,System.Threading.Tasks.Task{``13}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``13}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``15(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,System.Threading.CancellationToken,System.Threading.Tasks.Task{``14}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``14}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``16(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,System.Threading.CancellationToken,System.Threading.Tasks.Task{``15}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``15}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``4(System.Func{``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2},``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``5(System.Func{``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3},``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``6(System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4},``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``7(System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task{``6}},``0,``1,``2,``3,``4,``5)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5},``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``8(System.Func{``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task{``7}},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for Task based Func delegates.", Scope = "member", Target = "~M:Cuemon.TaskFuncFactory.Create``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Threading.Tasks.Task{``8}},``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.TaskFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7},``8}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "A Func designed for the Tester pattern. Microsoft allows 17 arguments in there Func impl; so do i here.", Scope = "type", Target = "~T:Cuemon.TesterFunc`10")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "A Func designed for the Tester pattern. Microsoft allows 17 arguments in there Func impl; so do i here.", Scope = "type", Target = "~T:Cuemon.TesterFunc`11")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "A Func designed for the Tester pattern. Microsoft allows 17 arguments in there Func impl; so do i here.", Scope = "type", Target = "~T:Cuemon.TesterFunc`12")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "A Func designed for the Tester pattern. Microsoft allows 17 arguments in there Func impl; so do i here.", Scope = "type", Target = "~T:Cuemon.TesterFunc`13")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "A Func designed for the Tester pattern. Microsoft allows 17 arguments in there Func impl; so do i here.", Scope = "type", Target = "~T:Cuemon.TesterFunc`14")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "A Func designed for the Tester pattern. Microsoft allows 17 arguments in there Func impl; so do i here.", Scope = "type", Target = "~T:Cuemon.TesterFunc`15")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "A Func designed for the Tester pattern. Microsoft allows 17 arguments in there Func impl; so do i here.", Scope = "type", Target = "~T:Cuemon.TesterFunc`16")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "A Func designed for the Tester pattern. Microsoft allows 17 arguments in there Func impl; so do i here.", Scope = "type", Target = "~T:Cuemon.TesterFunc`17")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "A Func designed for the Tester pattern. Microsoft allows 17 arguments in there Func impl; so do i here.", Scope = "type", Target = "~T:Cuemon.TesterFunc`18")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "A Func designed for the Tester pattern. Microsoft allows 17 arguments in there Func impl; so do i here.", Scope = "type", Target = "~T:Cuemon.TesterFunc`9")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``10(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7},``8,``9}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``10(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``0,``1,``2,``3,``4,``5,``6,``7)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7},``8,``9}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``11(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8},``9,``10}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``11(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``0,``1,``2,``3,``4,``5,``6,``7,``8)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8},``9,``10}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``12(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``10,``11}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``12(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``10,``11}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``13(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``11,``12}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``13(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``11,``12}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``14(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``12,``13}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``14(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11},``12,``13}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``15(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``13,``14}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``15(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12},``13,``14}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``16(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``14,``15}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``16(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13},``14,``15}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``17(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``15,``16}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``17(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14},``15,``16}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``18(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15},``16,``17}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``18(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15,``16,``17},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10,``11,``12,``13,``14,``15},``16,``17}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``4(Cuemon.TesterFunc{``0,``1,``2,``3},``0,``1)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1},``2,``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``5(Cuemon.TesterFunc{``0,``1,``2,``3,``4},``0,``1,``2)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2},``3,``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``6(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3},``4,``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``7(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6},``0,``1,``2,``3,``4)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4},``5,``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``8(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7},``0,``1,``2,``3,``4,``5)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5},``6,``7}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``9(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7,``8}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``9(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7,``8}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "type", Target = "~T:Cuemon.TesterFuncFactory`3")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``4(System.Func{``3},System.Func{``3,``0,``1,``2,``3},``0,``1,``2,System.Action{System.Exception,``0,``1,``2})~``3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{System.Exception,``0,``1,``2,``3})~``4")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``4(System.Func{``3},System.Func{``3,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] From 8d04dd11d8441b8a1952de24f0ee4c777b84a04f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 21:39:23 +0200 Subject: [PATCH 134/385] Quality gate; justifications for many generic arguments. --- src/Cuemon.Diagnostics/GlobalSuppressions.cs | 59 +++++++++++++++++++ src/Cuemon.Threading/GlobalSuppressions.cs | 62 ++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 src/Cuemon.Diagnostics/GlobalSuppressions.cs create mode 100644 src/Cuemon.Threading/GlobalSuppressions.cs diff --git a/src/Cuemon.Diagnostics/GlobalSuppressions.cs b/src/Cuemon.Diagnostics/GlobalSuppressions.cs new file mode 100644 index 000000000..c62df0804 --- /dev/null +++ b/src/Cuemon.Diagnostics/GlobalSuppressions.cs @@ -0,0 +1,59 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``4(System.Func{``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``5(System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``5(System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``6(System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``6(System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``7(System.Func{``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``7(System.Func{``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``8(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``8(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithActionAsync``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Threading.Tasks.Task{``9}},``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``9}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Threading.Tasks.Task{``9}},``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``9}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``11(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Threading.Tasks.Task{``10}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``10}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``11(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Threading.Tasks.Task{``10}},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``10}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``4(System.Func{``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``5(System.Func{``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``6(System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``6(System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``7(System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task{``6}},``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``7(System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task{``6}},``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``8(System.Func{``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task{``7}},``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``7}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``8(System.Func{``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task{``7}},``0,``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``7}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Threading.Tasks.Task{``8}},``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``8}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFuncAsync``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Threading.Tasks.Task{``8}},``0,``1,``2,``3,``4,``5,``6,``7,System.Threading.CancellationToken,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~System.Threading.Tasks.Task{Cuemon.Diagnostics.TimeMeasureProfiler{``8}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``10(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``10(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``4(System.Action{``0,``1,``2,``3},``0,``1,``2,``3,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``5(System.Action{``0,``1,``2,``3,``4},``0,``1,``2,``3,``4,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``6(System.Action{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,``5,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``6(System.Action{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,``5,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``7(System.Action{``0,``1,``2,``3,``4,``5,``6},``0,``1,``2,``3,``4,``5,``6,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``7(System.Action{``0,``1,``2,``3,``4,``5,``6},``0,``1,``2,``3,``4,``5,``6,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``8(System.Action{``0,``1,``2,``3,``4,``5,``6,``7},``0,``1,``2,``3,``4,``5,``6,``7,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``8(System.Action{``0,``1,``2,``3,``4,``5,``6,``7},``0,``1,``2,``3,``4,``5,``6,``7,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``9(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithAction``9(System.Action{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``9}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``10(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9},``0,``1,``2,``3,``4,``5,``6,``7,``8,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``9}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``11(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``10}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``11(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,``10},``0,``1,``2,``3,``4,``5,``6,``7,``8,``9,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``10}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``4(System.Func{``0,``1,``2,``3},``0,``1,``2,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``5(System.Func{``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``6(System.Func{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``7(System.Func{``0,``1,``2,``3,``4,``5,``6},``0,``1,``2,``3,``4,``5,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``7(System.Func{``0,``1,``2,``3,``4,``5,``6},``0,``1,``2,``3,``4,``5,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``8(System.Func{``0,``1,``2,``3,``4,``5,``6,``7},``0,``1,``2,``3,``4,``5,``6,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``7}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``8(System.Func{``0,``1,``2,``3,``4,``5,``6,``7},``0,``1,``2,``3,``4,``5,``6,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``7}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6,``7,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``8}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 10 generic arguments.", Scope = "member", Target = "~M:Cuemon.Diagnostics.TimeMeasure.WithFunc``9(System.Func{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6,``7,System.Action{Cuemon.Diagnostics.TimeMeasureOptions})~Cuemon.Diagnostics.TimeMeasureProfiler{``8}")] diff --git a/src/Cuemon.Threading/GlobalSuppressions.cs b/src/Cuemon.Threading/GlobalSuppressions.cs new file mode 100644 index 000000000..0908ff703 --- /dev/null +++ b/src/Cuemon.Threading/GlobalSuppressions.cs @@ -0,0 +1,62 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``1(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0},System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``2(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1},``1,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``3(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2},``1,``2,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3},``1,``2,``3,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3},``1,``2,``3,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3},``0,``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3},``0,``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3,``4},``1,``2,``3,``4,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3,``4},``1,``2,``3,``4,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``6(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``6(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForCoreAsync``2(``1,Cuemon.RelationalOperator,``1,Cuemon.AssignmentOperator,``1,Cuemon.ActionFactory{``0},System.Func{``1,Cuemon.RelationalOperator,``1,System.Boolean},System.Func{``1,Cuemon.AssignmentOperator,``1,``1},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``4(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3},``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``5(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3,``4},``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``6(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``6(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``4(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3},``1,``2,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``5(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4},``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``6(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``7(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``7(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``2(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1},System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``1}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``3(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2},``1,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``2}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``4(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3},``1,``2,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``4(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3},``1,``2,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``4(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3},``0,``1,``2,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4},``1,``2,``3,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4},``1,``2,``3,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``7(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``7(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultCoreAsync``3(``1,Cuemon.RelationalOperator,``1,Cuemon.AssignmentOperator,``1,Cuemon.FuncFactory{``0,``2},System.Func{``1,Cuemon.RelationalOperator,``1,System.Boolean},System.Func{``1,Cuemon.AssignmentOperator,``1,``1},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``2}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``4(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3},``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``5(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4},``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``5(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4},``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5},``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5},``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``4(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3},``2,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``5(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4},``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5},``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5},``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``8(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6,``7},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``7}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``8(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6,``7},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``7}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultCoreAsync``4(Cuemon.Threading.ForwardIterator{``0,``1},Cuemon.FuncFactory{``2,``3},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] From 547bad099c1046a7942ceb9d9a3ddf63f4a38294 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 22:45:25 +0200 Subject: [PATCH 135/385] Basic unit testing of Cuemon.Threading. --- test/Cuemon.Threading.Tests/ForAsyncTest.cs | 31 ++++++++++++ .../ForEachAsyncTest.cs | 32 +++++++++++++ .../ForEachResultAsyncTest.cs | 40 ++++++++++++++++ .../ForResultAsyncTest.cs | 38 +++++++++++++++ test/Cuemon.Threading.Tests/WhileAsyncTest.cs | 33 +++++++++++++ .../WhileResultAsyncTest.cs | 47 +++++++++++++++++++ 6 files changed, 221 insertions(+) create mode 100644 test/Cuemon.Threading.Tests/ForAsyncTest.cs create mode 100644 test/Cuemon.Threading.Tests/ForEachAsyncTest.cs create mode 100644 test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs create mode 100644 test/Cuemon.Threading.Tests/ForResultAsyncTest.cs create mode 100644 test/Cuemon.Threading.Tests/WhileAsyncTest.cs create mode 100644 test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs diff --git a/test/Cuemon.Threading.Tests/ForAsyncTest.cs b/test/Cuemon.Threading.Tests/ForAsyncTest.cs new file mode 100644 index 000000000..ae65f9245 --- /dev/null +++ b/test/Cuemon.Threading.Tests/ForAsyncTest.cs @@ -0,0 +1,31 @@ +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Threading +{ + public class ForAsyncTest : Test + { + public ForAsyncTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ForAsync_ShouldRunOn1000Threads() + { + var cb = new ConcurrentBag(); + await ParallelFactory.ForAsync(0, 1000, i => + { + Thread.Sleep(500); // todo: refactor to true async method + cb.Add(Thread.CurrentThread.ManagedThreadId); + }, o => o.PartitionSize = 1000); + + Assert.Equal(1000, cb.Count); + Assert.Equal(1000, cb.Distinct().Count()); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs b/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs new file mode 100644 index 000000000..cd2801483 --- /dev/null +++ b/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs @@ -0,0 +1,32 @@ +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Threading +{ + public class ForEachAsyncTest : Test + { + public ForEachAsyncTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ForEachAsyncTest_ShouldRunOn1000Threads() + { + var ic = Generate.RangeOf(1000, i => i); + var cb = new ConcurrentBag(); + await ParallelFactory.ForEachAsync(ic, i => + { + Thread.Sleep(500); // todo: refactor to true async method + cb.Add(Thread.CurrentThread.ManagedThreadId); + }, o => o.PartitionSize = 1000); + + Assert.Equal(1000, cb.Count); + Assert.Equal(1000, cb.Distinct().Count()); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs b/test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs new file mode 100644 index 000000000..cd2020d96 --- /dev/null +++ b/test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs @@ -0,0 +1,40 @@ +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Threading +{ + public class ForEachResultAsyncTest : Test + { + public ForEachResultAsyncTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ForEachResultAsyncTest_ShouldRunOn1000Threads() + { + var ic = Generate.RangeOf(1000, i => i); + var cb = new ConcurrentBag(); + var result = await ParallelFactory.ForEachResultAsync(ic, i => + { + Thread.Sleep(500); // todo: refactor to true async method + cb.Add(Thread.CurrentThread.ManagedThreadId); + return i; + }, o => o.PartitionSize = 1000); + + Assert.Equal(1000, result.Count); + Assert.Equal(1000, result.Distinct().Count()); + Assert.Equal(0, result.Min()); + Assert.Equal(999, result.Max()); + Assert.Equal(0, result.First()); + Assert.Equal(999, result.Last()); + + Assert.Equal(1000, cb.Count); + Assert.Equal(1000, cb.Distinct().Count()); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ForResultAsyncTest.cs b/test/Cuemon.Threading.Tests/ForResultAsyncTest.cs new file mode 100644 index 000000000..2c5c8ac27 --- /dev/null +++ b/test/Cuemon.Threading.Tests/ForResultAsyncTest.cs @@ -0,0 +1,38 @@ +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Threading +{ + public class ForResultAsyncTest : Test + { + public ForResultAsyncTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ForResultAsyncTest_ShouldRunOn1000Threads() + { + var cb = new ConcurrentBag(); + var result = await ParallelFactory.ForResultAsync(0, 1000, i => { + Thread.Sleep(500); // todo: refactor to true async method + cb.Add(Thread.CurrentThread.ManagedThreadId); + return i; + }, o => o.PartitionSize = 1000); + + Assert.Equal(1000, result.Count); + Assert.Equal(1000, result.Distinct().Count()); + Assert.Equal(0, result.Min()); + Assert.Equal(999, result.Max()); + Assert.Equal(0, result.First()); + Assert.Equal(999, result.Last()); + + Assert.Equal(1000, cb.Count); + Assert.Equal(1000, cb.Distinct().Count()); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/WhileAsyncTest.cs b/test/Cuemon.Threading.Tests/WhileAsyncTest.cs new file mode 100644 index 000000000..8cbb511ba --- /dev/null +++ b/test/Cuemon.Threading.Tests/WhileAsyncTest.cs @@ -0,0 +1,33 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Threading +{ + public class WhileAsyncTest : Test + { + public WhileAsyncTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task WhileAsyncTest_ShouldRunOn1000Threads() + { + var cb = new ConcurrentBag(); + var fakeReader = new ConcurrentQueue(Generate.RangeOf(1000, i => i)); + await ParallelFactory.WhileAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => cq.TryDequeue(out var x), i => + { + Thread.Sleep(500); // todo: refactor to true async method + cb.Add(Thread.CurrentThread.ManagedThreadId); + }, o => o.PartitionSize = 1000); + + Assert.Equal(1000, cb.Count); + Assert.Equal(1000, cb.Distinct().Count()); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs b/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs new file mode 100644 index 000000000..800712b40 --- /dev/null +++ b/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs @@ -0,0 +1,47 @@ +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Threading +{ + public class WhileResultAsyncTest : Test + { + public WhileResultAsyncTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task WhileResultAsyncTest_ShouldRunOn1000Threads() + { + var cb = new ConcurrentBag(); + var fakeReader = new ConcurrentQueue(Generate.RangeOf(1000, i => i)); + var result = await ParallelFactory.WhileResultAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => + { + if (cq.TryDequeue(out var x)) + { + return x; + } + return -1; + }, i => + { + Thread.Sleep(500); // todo: refactor to true async method + cb.Add(Thread.CurrentThread.ManagedThreadId); + return i; + }, o => o.PartitionSize = 1000); + + Assert.Equal(1000, result.Count); + Assert.Equal(1000, result.Distinct().Count()); + Assert.Equal(0, result.Min()); + Assert.Equal(999, result.Max()); + Assert.Equal(0, result.First()); + Assert.Equal(999, result.Last()); + + Assert.Equal(1000, cb.Count); + Assert.Equal(1000, cb.Distinct().Count()); + } + } +} \ No newline at end of file From 30b6e9652278a53b0b501e41092cf25ae754a282 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 23:07:20 +0200 Subject: [PATCH 136/385] Updating package release notes. --- docfx/api/namespaces/Cuemon.Threading.md | 4 +++- src/Cuemon.Core/Properties/PackageReleaseNotes.txt | 3 +++ .../Properties/PackageReleaseNotes.txt | 6 +++--- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 14 ++++++++++++++ 5 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 src/Cuemon.Threading/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Threading.md b/docfx/api/namespaces/Cuemon.Threading.md index 86b0ec81e..1b98b62b5 100644 --- a/docfx/api/namespaces/Cuemon.Threading.md +++ b/docfx/api/namespaces/Cuemon.Threading.md @@ -2,4 +2,6 @@ uid: Cuemon.Threading summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Threading namespace contains types that can prove helpful when working with concurrent operations. The namespace relates to the System.Threading namespace. + +Availability: NET Standard 2.0 \ No newline at end of file diff --git a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt index a3a8fa38a..ea0ae80e5 100644 --- a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt @@ -1,4 +1,6 @@ 6.0.0 +Availability: NET Standard 2.0 + # Upgrade Steps - [ACTION REQUIRED] - @@ -6,6 +8,7 @@ # Breaking Changes - REMOVED StringFormatter class in the Cuemon namespace - REMOVED StandardizedDateTimeFormatPattern enum in the Cuemon namespace +- MOVED AsyncOptions class in the Cuemon.Threading namespace to its own assembly (by the same name and namespace) # New Features - diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt index 1df811961..0750101eb 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt @@ -2,6 +2,6 @@ Availability: NET Standard 2.0, NET Core 3.0 # New Features -- Added HostTest class in the Cuemon.Extensions.Xunit.Hosting namespace that represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection, should derive -- Added IHostFixture interface in the Cuemon.Extensions.Xunit.Hosting namespace that provides a way to use Microsoft Dependency Injection in unit tests -- Added HostFixture class in the Cuemon.Extensions.Xunit.Hosting namespace that provides a default implementation of the IHostFixture interface \ No newline at end of file +- ADDED HostTest class in the Cuemon.Extensions.Xunit.Hosting namespace that represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection, should derive +- ADDED IHostFixture interface in the Cuemon.Extensions.Xunit.Hosting namespace that provides a way to use Microsoft Dependency Injection in unit tests +- ADDED HostFixture class in the Cuemon.Extensions.Xunit.Hosting namespace that provides a default implementation of the IHostFixture interface \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt index 82cfa98d4..f387233ea 100644 --- a/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt @@ -2,4 +2,4 @@ Availability: NET Standard 2.0 # New Features -- Added Test class in the Cuemon.Extensions.Xunit namespace that represents the base class from which all implementations of unit testing should derive \ No newline at end of file +- ADDED Test class in the Cuemon.Extensions.Xunit namespace that represents the base class from which all implementations of unit testing should derive \ No newline at end of file diff --git a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..0ffca99b1 --- /dev/null +++ b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt @@ -0,0 +1,14 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 + +# New Features +- ADDED TimerFactory class in the Cuemon.Threading namespace that provides access to factory methods for creating and configuring Timer instances +- ADDED AsyncOptions class in the Cuemon.Threading namespace that specifies options that is related to asynchronous operations + +# Bug Fixes +- APPLIED ConfigureAwait(false) to all async methods + +# Quality Actions +- APPLIED while loop over for loop https://rules.sonarsource.com/csharp/RSPEC-1264 +- JUSTIFIED that types are allowed to have many generic parameters https://rules.sonarsource.com/csharp/RSPEC-2436 +- JUSTIFIED that methods are allowed to have many generic parameters https://rules.sonarsource.com/csharp/RSPEC-107 \ No newline at end of file From 50f7f06794ea52b1036d4ff5ffbfee2a373902d2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 23:07:30 +0200 Subject: [PATCH 137/385] Updating package description. --- src/Cuemon.Threading/Cuemon.Threading.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Threading/Cuemon.Threading.csproj b/src/Cuemon.Threading/Cuemon.Threading.csproj index 29a6fb51b..85995d628 100644 --- a/src/Cuemon.Threading/Cuemon.Threading.csproj +++ b/src/Cuemon.Threading/Cuemon.Threading.csproj @@ -8,7 +8,7 @@ Cuemon.Threading Cuemon.Threading - The Cuemon.Threading namespace contains features related to the System.Threading namespace. + The Cuemon.Threading namespace contains types that can prove helpful when working with concurrent operations. The namespace relates to the System.Threading namespace. parallel-factory for-async for-each-async for-each-result-async for-result-async while-async while-result-async From b45db85c0f143e728cc49b5bb6f58e73e444b4ac Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 23:53:25 +0200 Subject: [PATCH 138/385] OCD change ;-) --- src/Cuemon.Threading/ForwardIterator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cuemon.Threading/ForwardIterator.cs b/src/Cuemon.Threading/ForwardIterator.cs index 838f08630..cc2899738 100644 --- a/src/Cuemon.Threading/ForwardIterator.cs +++ b/src/Cuemon.Threading/ForwardIterator.cs @@ -8,13 +8,13 @@ internal class ForwardIterator internal ForwardIterator(TReader reader, Func> condition, Func provider) { Reader = reader; - Condition = condition; + ConditionAsync = condition; Provider = provider; } private TReader Reader { get; } - private Func> Condition { get; } + private Func> ConditionAsync { get; } private Func Provider { get; } @@ -22,7 +22,7 @@ internal ForwardIterator(TReader reader, Func> condition, Func ReadAsync() { - if (await Condition().ConfigureAwait(false)) + if (await ConditionAsync().ConfigureAwait(false)) { Current = Provider(Reader); return true; From 530647eb48911f9bf3ef27ad50230ae9b52c55a3 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 6 Sep 2020 23:54:29 +0200 Subject: [PATCH 139/385] Changed test to fixed number of threads. --- test/Cuemon.Threading.Tests/ForAsyncTest.cs | 6 ++---- test/Cuemon.Threading.Tests/ForEachAsyncTest.cs | 5 ++--- test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs | 5 ++--- test/Cuemon.Threading.Tests/ForResultAsyncTest.cs | 5 ++--- test/Cuemon.Threading.Tests/WhileAsyncTest.cs | 5 ++--- test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs | 5 ++--- 6 files changed, 12 insertions(+), 19 deletions(-) diff --git a/test/Cuemon.Threading.Tests/ForAsyncTest.cs b/test/Cuemon.Threading.Tests/ForAsyncTest.cs index ae65f9245..c1efe28ff 100644 --- a/test/Cuemon.Threading.Tests/ForAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ForAsyncTest.cs @@ -1,5 +1,4 @@ using System.Collections.Concurrent; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Cuemon.Extensions.Xunit; @@ -20,12 +19,11 @@ public async Task ForAsync_ShouldRunOn1000Threads() var cb = new ConcurrentBag(); await ParallelFactory.ForAsync(0, 1000, i => { - Thread.Sleep(500); // todo: refactor to true async method + Thread.Sleep(50); // todo: refactor to true async method cb.Add(Thread.CurrentThread.ManagedThreadId); - }, o => o.PartitionSize = 1000); + }, o => o.PartitionSize = 64); Assert.Equal(1000, cb.Count); - Assert.Equal(1000, cb.Distinct().Count()); } } } \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs b/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs index cd2801483..994b72bf0 100644 --- a/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs @@ -21,12 +21,11 @@ public async Task ForEachAsyncTest_ShouldRunOn1000Threads() var cb = new ConcurrentBag(); await ParallelFactory.ForEachAsync(ic, i => { - Thread.Sleep(500); // todo: refactor to true async method + Thread.Sleep(50); // todo: refactor to true async method cb.Add(Thread.CurrentThread.ManagedThreadId); - }, o => o.PartitionSize = 1000); + }, o => o.PartitionSize = 64); Assert.Equal(1000, cb.Count); - Assert.Equal(1000, cb.Distinct().Count()); } } } \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs b/test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs index cd2020d96..509d2e46e 100644 --- a/test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs @@ -21,10 +21,10 @@ public async Task ForEachResultAsyncTest_ShouldRunOn1000Threads() var cb = new ConcurrentBag(); var result = await ParallelFactory.ForEachResultAsync(ic, i => { - Thread.Sleep(500); // todo: refactor to true async method + Thread.Sleep(50); // todo: refactor to true async method cb.Add(Thread.CurrentThread.ManagedThreadId); return i; - }, o => o.PartitionSize = 1000); + }, o => o.PartitionSize = 64); Assert.Equal(1000, result.Count); Assert.Equal(1000, result.Distinct().Count()); @@ -34,7 +34,6 @@ public async Task ForEachResultAsyncTest_ShouldRunOn1000Threads() Assert.Equal(999, result.Last()); Assert.Equal(1000, cb.Count); - Assert.Equal(1000, cb.Distinct().Count()); } } } \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ForResultAsyncTest.cs b/test/Cuemon.Threading.Tests/ForResultAsyncTest.cs index 2c5c8ac27..e02e50368 100644 --- a/test/Cuemon.Threading.Tests/ForResultAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ForResultAsyncTest.cs @@ -19,10 +19,10 @@ public async Task ForResultAsyncTest_ShouldRunOn1000Threads() { var cb = new ConcurrentBag(); var result = await ParallelFactory.ForResultAsync(0, 1000, i => { - Thread.Sleep(500); // todo: refactor to true async method + Thread.Sleep(50); // todo: refactor to true async method cb.Add(Thread.CurrentThread.ManagedThreadId); return i; - }, o => o.PartitionSize = 1000); + }, o => o.PartitionSize = 64); Assert.Equal(1000, result.Count); Assert.Equal(1000, result.Distinct().Count()); @@ -32,7 +32,6 @@ public async Task ForResultAsyncTest_ShouldRunOn1000Threads() Assert.Equal(999, result.Last()); Assert.Equal(1000, cb.Count); - Assert.Equal(1000, cb.Distinct().Count()); } } } \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/WhileAsyncTest.cs b/test/Cuemon.Threading.Tests/WhileAsyncTest.cs index 8cbb511ba..3c6eb4f26 100644 --- a/test/Cuemon.Threading.Tests/WhileAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/WhileAsyncTest.cs @@ -22,12 +22,11 @@ public async Task WhileAsyncTest_ShouldRunOn1000Threads() var fakeReader = new ConcurrentQueue(Generate.RangeOf(1000, i => i)); await ParallelFactory.WhileAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => cq.TryDequeue(out var x), i => { - Thread.Sleep(500); // todo: refactor to true async method + Thread.Sleep(50); // todo: refactor to true async method cb.Add(Thread.CurrentThread.ManagedThreadId); - }, o => o.PartitionSize = 1000); + }, o => o.PartitionSize = 64); Assert.Equal(1000, cb.Count); - Assert.Equal(1000, cb.Distinct().Count()); } } } \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs b/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs index 800712b40..9dd383c5b 100644 --- a/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs @@ -28,10 +28,10 @@ public async Task WhileResultAsyncTest_ShouldRunOn1000Threads() return -1; }, i => { - Thread.Sleep(500); // todo: refactor to true async method + Thread.Sleep(50); // todo: refactor to true async method cb.Add(Thread.CurrentThread.ManagedThreadId); return i; - }, o => o.PartitionSize = 1000); + }, o => o.PartitionSize = 64); Assert.Equal(1000, result.Count); Assert.Equal(1000, result.Distinct().Count()); @@ -41,7 +41,6 @@ public async Task WhileResultAsyncTest_ShouldRunOn1000Threads() Assert.Equal(999, result.Last()); Assert.Equal(1000, cb.Count); - Assert.Equal(1000, cb.Distinct().Count()); } } } \ No newline at end of file From 57eb02025f46f631979b683211d528e16de78ea9 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 7 Sep 2020 00:37:35 +0200 Subject: [PATCH 140/385] Added a white-space character (ALT+255) for "empty" lines are not ignored. --- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Threading/Properties/PackageReleaseNotes.txt | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt index 0750101eb..20cb28c1c 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt @@ -1,6 +1,6 @@ Version: 6.0.0 Availability: NET Standard 2.0, NET Core 3.0 - +  # New Features - ADDED HostTest class in the Cuemon.Extensions.Xunit.Hosting namespace that represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection, should derive - ADDED IHostFixture interface in the Cuemon.Extensions.Xunit.Hosting namespace that provides a way to use Microsoft Dependency Injection in unit tests diff --git a/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt index f387233ea..05ca34de5 100644 --- a/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 Availability: NET Standard 2.0 - +  # New Features - ADDED Test class in the Cuemon.Extensions.Xunit namespace that represents the base class from which all implementations of unit testing should derive \ No newline at end of file diff --git a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt index 0ffca99b1..e02a5a7e2 100644 --- a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt @@ -1,13 +1,13 @@ Version: 6.0.0 Availability: NET Standard 2.0 - +  # New Features - ADDED TimerFactory class in the Cuemon.Threading namespace that provides access to factory methods for creating and configuring Timer instances - ADDED AsyncOptions class in the Cuemon.Threading namespace that specifies options that is related to asynchronous operations - +  # Bug Fixes - APPLIED ConfigureAwait(false) to all async methods - +  # Quality Actions - APPLIED while loop over for loop https://rules.sonarsource.com/csharp/RSPEC-1264 - JUSTIFIED that types are allowed to have many generic parameters https://rules.sonarsource.com/csharp/RSPEC-2436 From f6504a4e66e876d7b39db99132226434d253f34f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 7 Sep 2020 00:37:54 +0200 Subject: [PATCH 141/385] Added variables to package release notes. --- Directory.Build.targets | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 4dbcaa36d..ff27971c6 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -8,12 +8,16 @@ - - + + $(MSBuildProjectDirectory)/Properties/PackageReleaseNotes.txt + + + + - @(PackageReleaseNotesLines, '%0a') + @(PackageReleaseNotesLines, '%0A') \ No newline at end of file From 2470180e51dac2a8be473a7bea36a47cf8a52baf Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 7 Sep 2020 00:45:22 +0200 Subject: [PATCH 142/385] ALT+255 --- .../Properties/PackageReleaseNotes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt index 026fef672..7110fc924 100644 --- a/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt @@ -1,6 +1,6 @@ Version: 6.0.0 Availability: NET Standard 2.0, NET Core 3.0 - +  # New Features - Added extension methods for IHostEnvironment: IsLocalDevelopment and IsNonProduction - Added extension methods for IHostingEnvironment: IsLocalDevelopment and IsNonProduction \ No newline at end of file From db5d7b080efc1f4fd7221408b04c650651655179 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 7 Sep 2020 00:45:44 +0200 Subject: [PATCH 143/385] Included Cuemon.Extensions.Hosting for netcoreapp3 build. --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ae1eb8c6c..4c6dd119f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -92,6 +92,7 @@ jobs: projects: | src/**/Cuemon.AspNetCore*.csproj src/**/Cuemon.Extensions.AspNetCore*.csproj + src/**/Cuemon.Extensions.Hosting.csproj src/**/Cuemon.Extensions.Xunit.Hosting.csproj arguments: '--configuration $(BuildConfiguration) --no-restore --framework netcoreapp3.0' workingDirectory: '$(BuildSource)' From af321baa68984e74ae1c791fe6030af450279c17 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 7 Sep 2020 02:21:39 +0200 Subject: [PATCH 144/385] Removed unused variable. --- src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs b/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs index 6616be6e7..024731f1b 100644 --- a/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs +++ b/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs @@ -166,7 +166,6 @@ public static Stream ToStream(this IDecorator decorator, Action ToStreamAsync(this IDecorator decorator, CancellationToken ct = default, Action setup = null) { Validator.ThrowIfNull(decorator, nameof(decorator)); - var options = Patterns.Configure(setup); return Disposable.SafeInvokeAsync(() => new MemoryStream(), async (ms, token) => { var bytes = Convertible.GetBytes(decorator.Inner, setup); From 2c2520386a80397fe4d8aab4ba79471c1161d56a Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 7 Sep 2020 02:24:18 +0200 Subject: [PATCH 145/385] Reduced codesmell by moving preprocessor directives. --- .../HostEnvironmentExtensions.cs | 9 +++++---- .../HostingEnvironmentExtensions.cs | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Cuemon.Extensions.Hosting/HostEnvironmentExtensions.cs b/src/Cuemon.Extensions.Hosting/HostEnvironmentExtensions.cs index add72840e..7791b89b5 100644 --- a/src/Cuemon.Extensions.Hosting/HostEnvironmentExtensions.cs +++ b/src/Cuemon.Extensions.Hosting/HostEnvironmentExtensions.cs @@ -1,8 +1,9 @@ -using Microsoft.Extensions.Hosting; +#if NETCOREAPP +using Microsoft.Extensions.Hosting; namespace Cuemon.Extensions.Hosting { - #if NETCOREAPP + /// /// Extension methods for the interface. /// @@ -28,5 +29,5 @@ public static bool IsNonProduction(this IHostEnvironment environment) return !environment.IsProduction(); } } - #endif -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/Cuemon.Extensions.Hosting/HostingEnvironmentExtensions.cs b/src/Cuemon.Extensions.Hosting/HostingEnvironmentExtensions.cs index fe92e972d..983a99d9d 100644 --- a/src/Cuemon.Extensions.Hosting/HostingEnvironmentExtensions.cs +++ b/src/Cuemon.Extensions.Hosting/HostingEnvironmentExtensions.cs @@ -1,8 +1,8 @@ -using Microsoft.Extensions.Hosting; +#if NETSTANDARD +using Microsoft.Extensions.Hosting; namespace Cuemon.Extensions.Hosting { - #if NETSTANDARD /// /// Extension methods for the interface. /// @@ -28,5 +28,5 @@ public static bool IsNonProduction(this IHostingEnvironment environment) return !environment.IsProduction(); } } - #endif -} \ No newline at end of file +} +#endif \ No newline at end of file From 7de6f3492d4c45205d2b93d2aa50f967f2ba1646 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 7 Sep 2020 02:27:13 +0200 Subject: [PATCH 146/385] New justifications for S107, S2436 --- src/Cuemon.Core/GlobalSuppressions.cs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Cuemon.Core/GlobalSuppressions.cs b/src/Cuemon.Core/GlobalSuppressions.cs index 90e17d5f1..eb4fe95f7 100644 --- a/src/Cuemon.Core/GlobalSuppressions.cs +++ b/src/Cuemon.Core/GlobalSuppressions.cs @@ -238,12 +238,19 @@ [assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``9(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7,``8}")] [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``9(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7,``8}")] [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "type", Target = "~T:Cuemon.TesterFuncFactory`3")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``4(System.Func{``3},System.Func{``3,``0,``1,``2,``3},``0,``1,``2,System.Action{System.Exception,``0,``1,``2})~``3")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{System.Exception,``0,``1,``2,``3})~``4")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``4(System.Func{``3},System.Func{``3,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``3}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; when something is truly generic, allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``4(System.Func{``3},System.Func{``3,``0,``1,``2,``3},``0,``1,``2,System.Action{System.Exception,``0,``1,``2})~``3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{System.Exception,``0,``1,``2,``3})~``4")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``4(System.Func{``3},System.Func{``3,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``4(System.Boolean,System.Action{``0,``1,``2,``3},System.Action{``0,``1,``2,``3},``0,``1,``2,``3)")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``5(System.Boolean,System.Action{``0,``1,``2,``3,``4},System.Action{``0,``1,``2,``3,``4},``0,``1,``2,``3,``4)")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``5(System.Boolean,System.Action{``0,``1,``2,``3,``4},System.Action{``0,``1,``2,``3,``4},``0,``1,``2,``3,``4)")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``4(System.Boolean,System.Func{``0,``1,``2,``3},System.Func{``0,``1,``2,``3},``0,``1,``2)~``3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``5(System.Boolean,System.Func{``0,``1,``2,``3,``4},System.Func{``0,``1,``2,``3,``4},``0,``1,``2,``3)~``4")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``6(System.Boolean,System.Func{``0,``1,``2,``3,``4,``5},System.Func{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4)~``5")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``6(System.Boolean,System.Func{``0,``1,``2,``3,``4,``5},System.Func{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4)~``5")] From 4d902c20c4f352612dc0c25b3e405771bdede7f2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 00:33:33 +0200 Subject: [PATCH 147/385] S107, S2436 justifications. --- src/Cuemon.Core/GlobalSuppressions.cs | 35 +++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/Cuemon.Core/GlobalSuppressions.cs b/src/Cuemon.Core/GlobalSuppressions.cs index eb4fe95f7..1f79ad696 100644 --- a/src/Cuemon.Core/GlobalSuppressions.cs +++ b/src/Cuemon.Core/GlobalSuppressions.cs @@ -238,19 +238,22 @@ [assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``9(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7,``8}")] [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``9(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7,``8}")] [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "type", Target = "~T:Cuemon.TesterFuncFactory`3")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``4(System.Func{``3},System.Func{``3,``0,``1,``2,``3},``0,``1,``2,System.Action{System.Exception,``0,``1,``2})~``3")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{System.Exception,``0,``1,``2,``3})~``4")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``4(System.Func{``3},System.Func{``3,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``3}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``4(System.Boolean,System.Action{``0,``1,``2,``3},System.Action{``0,``1,``2,``3},``0,``1,``2,``3)")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``5(System.Boolean,System.Action{``0,``1,``2,``3,``4},System.Action{``0,``1,``2,``3,``4},``0,``1,``2,``3,``4)")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``5(System.Boolean,System.Action{``0,``1,``2,``3,``4},System.Action{``0,``1,``2,``3,``4},``0,``1,``2,``3,``4)")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``4(System.Boolean,System.Func{``0,``1,``2,``3},System.Func{``0,``1,``2,``3},``0,``1,``2)~``3")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``5(System.Boolean,System.Func{``0,``1,``2,``3,``4},System.Func{``0,``1,``2,``3,``4},``0,``1,``2,``3)~``4")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``6(System.Boolean,System.Func{``0,``1,``2,``3,``4,``5},System.Func{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4)~``5")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``6(System.Boolean,System.Func{``0,``1,``2,``3,``4,``5},System.Func{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4)~``5")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``4(System.Func{``3},System.Func{``3,``0,``1,``2,``3},``0,``1,``2,System.Action{System.Exception,``0,``1,``2})~``3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{System.Exception,``0,``1,``2,``3})~``4")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``4(System.Func{``3},System.Func{``3,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``4(System.Boolean,System.Action{``0,``1,``2,``3},System.Action{``0,``1,``2,``3},``0,``1,``2,``3)")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``5(System.Boolean,System.Action{``0,``1,``2,``3,``4},System.Action{``0,``1,``2,``3,``4},``0,``1,``2,``3,``4)")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``5(System.Boolean,System.Action{``0,``1,``2,``3,``4},System.Action{``0,``1,``2,``3,``4},``0,``1,``2,``3,``4)")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``4(System.Boolean,System.Func{``0,``1,``2,``3},System.Func{``0,``1,``2,``3},``0,``1,``2)~``3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``5(System.Boolean,System.Func{``0,``1,``2,``3,``4},System.Func{``0,``1,``2,``3,``4},``0,``1,``2,``3)~``4")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``6(System.Boolean,System.Func{``0,``1,``2,``3,``4,``5},System.Func{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4)~``5")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Condition.TernaryIf``6(System.Boolean,System.Func{``0,``1,``2,``3,``4,``5},System.Func{``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4)~``5")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Reflection.ActivatorFactory.CreateInstance``4(``0,``1,``2,System.Action{Cuemon.Reflection.ActivatorOptions})~``3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Reflection.ActivatorFactory.CreateInstance``5(``0,``1,``2,``3,System.Action{Cuemon.Reflection.ActivatorOptions})~``4")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Reflection.ActivatorFactory.CreateInstance``6(``0,``1,``2,``3,``4,System.Action{Cuemon.Reflection.ActivatorOptions})~``5")] From 2b1372c95e5e20bca5925506e1a112b5601fc3d2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 00:33:53 +0200 Subject: [PATCH 148/385] Cleanup. --- src/Cuemon.Data/GlobalSuppressions.cs | 3 ++- src/Cuemon.Xml/GlobalSuppressions.cs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Data/GlobalSuppressions.cs b/src/Cuemon.Data/GlobalSuppressions.cs index d5b468c7b..ed16c5634 100644 --- a/src/Cuemon.Data/GlobalSuppressions.cs +++ b/src/Cuemon.Data/GlobalSuppressions.cs @@ -11,4 +11,5 @@ [assembly: SuppressMessage("Major Code Smell", "S1168:Empty arrays and collections should be returned instead of null", Justification = "By design; property serves it purpose.", Scope = "member", Target = "~P:Cuemon.Data.ConcurrentDsvDataReader.NullRead")] [assembly: SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "By design to help clarify context.", Scope = "member", Target = "~M:Cuemon.Data.ConcurrentDsvDataReader.ReadNext(System.String[])~System.String[]")] [assembly: SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "By design to help clarify context.", Scope = "member", Target = "~M:Cuemon.Data.DsvDataReader.ReadNext(System.String[])~System.String[]")] -[assembly: SuppressMessage("Major Code Smell", "S1168:Empty arrays and collections should be returned instead of null", Justification = "By design; property serves it purpose.", Scope = "member", Target = "~P:Cuemon.Data.DsvDataReader.NullRead")] \ No newline at end of file +[assembly: SuppressMessage("Major Code Smell", "S1168:Empty arrays and collections should be returned instead of null", Justification = "By design; property serves it purpose.", Scope = "member", Target = "~P:Cuemon.Data.DsvDataReader.NullRead")] +[assembly: SuppressMessage("Major Code Smell", "S907:\"goto\" statement should not be used", Justification = "Legacy implementation.", Scope = "member", Target = "~M:Cuemon.Data.Xml.XmlDataReader.ReadNext(System.Boolean)~System.Boolean")] \ No newline at end of file diff --git a/src/Cuemon.Xml/GlobalSuppressions.cs b/src/Cuemon.Xml/GlobalSuppressions.cs index db0c78032..291aaa197 100644 --- a/src/Cuemon.Xml/GlobalSuppressions.cs +++ b/src/Cuemon.Xml/GlobalSuppressions.cs @@ -6,6 +6,5 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Major Code Smell", "S907:\"goto\" statement should not be used", Justification = "Legacy implementation.", Scope = "member", Target = "~M:Cuemon.Xml.Serialization.Converters.DefaultXmlConverter.ParseReadXmlDefault(System.Xml.XmlReader,System.Type)~System.Object")] -[assembly: SuppressMessage("Major Code Smell", "S907:\"goto\" statement should not be used", Justification = "Legacy implementation.", Scope = "member", Target = "~M:Cuemon.Xml.XmlDataReader.ReadNext(System.Boolean)~System.Boolean")] [assembly: SuppressMessage("Major Code Smell", "S907:\"goto\" statement should not be used", Justification = "Legacy implementation.", Scope = "member", Target = "~M:Cuemon.Xml.XmlReaderDecoratorExtensions.ToHierarchy(Cuemon.IDecorator{System.Xml.XmlReader})~Cuemon.IHierarchy{Cuemon.DataPair}")] [assembly: SuppressMessage("Minor Code Smell", "S3626:Jump statements should not be redundant", Justification = "False-positive.", Scope = "member", Target = "~M:Cuemon.Xml.XmlStreamFactory.CreateStream(System.Action{System.Xml.XmlWriter},System.Action{System.Xml.XmlWriterSettings})~System.IO.Stream")] From 6e4e3cd657ea7075c72447e5ea28df1ed8e41754 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 00:34:37 +0200 Subject: [PATCH 149/385] Added Analysis. --- src/Cuemon.Threading/Properties/PackageReleaseNotes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt index e02a5a7e2..04306ea5c 100644 --- a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt @@ -8,7 +8,7 @@ Availability: NET Standard 2.0 # Bug Fixes - APPLIED ConfigureAwait(false) to all async methods   -# Quality Actions +# Quality Analysis Actions - APPLIED while loop over for loop https://rules.sonarsource.com/csharp/RSPEC-1264 - JUSTIFIED that types are allowed to have many generic parameters https://rules.sonarsource.com/csharp/RSPEC-2436 - JUSTIFIED that methods are allowed to have many generic parameters https://rules.sonarsource.com/csharp/RSPEC-107 \ No newline at end of file From 7ebb174089aaf21b87a6aca0dabb3a5a4f4ec989 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 00:35:14 +0200 Subject: [PATCH 150/385] Removed null copy-paste. --- test/Cuemon.AspNetCore.Tests/Http/Headers/ExceptionTest.cs | 2 +- test/Cuemon.AspNetCore.Tests/Http/Throttling/ExceptionTest.cs | 2 +- test/Cuemon.Core.Tests/CalculatorTest.cs | 2 +- test/Cuemon.Core.Tests/DisposableTest.cs | 2 +- test/Cuemon.Core.Tests/ExceptionTest.cs | 2 +- test/Cuemon.Core.Tests/GenerateTest.cs | 2 +- test/Cuemon.Core.Tests/ObjectDecoratorExtensionsTest.cs | 2 +- .../Reflection/AssemblyDecoratorExtensionsTest.cs | 2 +- .../Reflection/MethodInfoDecoratorExtensionsTest.cs | 2 +- .../Reflection/PropertyInfoDecoratorExtensionsTest.cs | 2 +- test/Cuemon.Core.Tests/Security/HashFactoryTest.cs | 2 +- test/Cuemon.Core.Tests/StringDecoratorExtensionsTest.cs | 2 +- test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs | 2 +- test/Cuemon.Core.Tests/ValidatorTest.cs | 2 +- test/Cuemon.Data.Tests/ConcurrentDsvDataReaderTest.cs | 2 +- test/Cuemon.Data.Tests/DsvDataReaderTest.cs | 2 +- test/Cuemon.Data.Tests/ExceptionTest.cs | 2 +- test/Cuemon.Extensions.Core.Tests/ValidatorExtensionsTest.cs | 2 +- test/Cuemon.Net.Tests/QueryStringCollectionTest.cs | 2 +- test/Cuemon.Resilience.Tests/ExceptionTest.cs | 2 +- test/Cuemon.Resilience.Tests/TransientOperationTest.cs | 2 +- test/Cuemon.Security.Cryptography.Tests/AesCryptorTest.cs | 2 +- test/Cuemon.Security.Cryptography.Tests/KeyedHashFactoryTest.cs | 2 +- .../UnkeyedHashFactoryTest.cs | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/ExceptionTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/ExceptionTest.cs index 792254818..555bc440c 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/ExceptionTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/ExceptionTest.cs @@ -8,7 +8,7 @@ namespace Cuemon.AspNetCore.Http.Headers { public class ExceptionTest : Test { - public ExceptionTest(ITestOutputHelper output = null) : base(output) + public ExceptionTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ExceptionTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ExceptionTest.cs index 842d9092d..2b9d13ee7 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ExceptionTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ExceptionTest.cs @@ -9,7 +9,7 @@ namespace Cuemon.AspNetCore.Http.Throttling { public class ExceptionTest : Test { - public ExceptionTest(ITestOutputHelper output = null) : base(output) + public ExceptionTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/CalculatorTest.cs b/test/Cuemon.Core.Tests/CalculatorTest.cs index 5d167c728..4878f71a9 100644 --- a/test/Cuemon.Core.Tests/CalculatorTest.cs +++ b/test/Cuemon.Core.Tests/CalculatorTest.cs @@ -6,7 +6,7 @@ namespace Cuemon { public class CalculatorTest : Test { - public CalculatorTest(ITestOutputHelper output = null) : base(output) + public CalculatorTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/DisposableTest.cs b/test/Cuemon.Core.Tests/DisposableTest.cs index d94903113..bc073e6b1 100644 --- a/test/Cuemon.Core.Tests/DisposableTest.cs +++ b/test/Cuemon.Core.Tests/DisposableTest.cs @@ -12,7 +12,7 @@ namespace Cuemon { public class DisposableTest : Test { - public DisposableTest(ITestOutputHelper output = null) : base(output) + public DisposableTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/ExceptionTest.cs b/test/Cuemon.Core.Tests/ExceptionTest.cs index 94f3bb306..e3216d11c 100644 --- a/test/Cuemon.Core.Tests/ExceptionTest.cs +++ b/test/Cuemon.Core.Tests/ExceptionTest.cs @@ -8,7 +8,7 @@ namespace Cuemon { public class ExceptionTest : Test { - public ExceptionTest(ITestOutputHelper output = null) : base(output) + public ExceptionTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/GenerateTest.cs b/test/Cuemon.Core.Tests/GenerateTest.cs index 0bfb84597..94608390a 100644 --- a/test/Cuemon.Core.Tests/GenerateTest.cs +++ b/test/Cuemon.Core.Tests/GenerateTest.cs @@ -8,7 +8,7 @@ namespace Cuemon { public class GenerateTest : Test { - public GenerateTest(ITestOutputHelper output = null) : base(output) + public GenerateTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/ObjectDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/ObjectDecoratorExtensionsTest.cs index d1fc91e1b..fef123421 100644 --- a/test/Cuemon.Core.Tests/ObjectDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/ObjectDecoratorExtensionsTest.cs @@ -10,7 +10,7 @@ public class ObjectDecoratorExtensionsTest : Test { private readonly string _number = $"{Generate.RandomString(5, Alphanumeric.Numbers)},{Generate.RandomNumber(0, 99):D2}"; - public ObjectDecoratorExtensionsTest(ITestOutputHelper output = null) : base(output) + public ObjectDecoratorExtensionsTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs index 9d6fcffc8..1dc476c38 100644 --- a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs @@ -9,7 +9,7 @@ namespace Cuemon.Reflection { public class AssemblyDecoratorExtensionsTest : Test { - public AssemblyDecoratorExtensionsTest(ITestOutputHelper output = null) : base(output) + public AssemblyDecoratorExtensionsTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/Reflection/MethodInfoDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Reflection/MethodInfoDecoratorExtensionsTest.cs index 7ee729a97..3c307b210 100644 --- a/test/Cuemon.Core.Tests/Reflection/MethodInfoDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Reflection/MethodInfoDecoratorExtensionsTest.cs @@ -7,7 +7,7 @@ namespace Cuemon.Reflection { public class MethodInfoDecoratorExtensionsTest : Test { - public MethodInfoDecoratorExtensionsTest(ITestOutputHelper output = null) : base(output) + public MethodInfoDecoratorExtensionsTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/Reflection/PropertyInfoDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Reflection/PropertyInfoDecoratorExtensionsTest.cs index bfc9a00ab..342ee1555 100644 --- a/test/Cuemon.Core.Tests/Reflection/PropertyInfoDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Reflection/PropertyInfoDecoratorExtensionsTest.cs @@ -8,7 +8,7 @@ namespace Cuemon.Reflection { public class PropertyInfoDecoratorExtensionsTest : Test { - public PropertyInfoDecoratorExtensionsTest(ITestOutputHelper output = null) : base(output) + public PropertyInfoDecoratorExtensionsTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/Security/HashFactoryTest.cs b/test/Cuemon.Core.Tests/Security/HashFactoryTest.cs index f39c64b2b..7c9052e01 100644 --- a/test/Cuemon.Core.Tests/Security/HashFactoryTest.cs +++ b/test/Cuemon.Core.Tests/Security/HashFactoryTest.cs @@ -7,7 +7,7 @@ namespace Cuemon.Security { public class HashFactoryTest : Test { - public HashFactoryTest(ITestOutputHelper output = null) : base(output) + public HashFactoryTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/StringDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/StringDecoratorExtensionsTest.cs index f07053d9e..b33160105 100644 --- a/test/Cuemon.Core.Tests/StringDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/StringDecoratorExtensionsTest.cs @@ -10,7 +10,7 @@ namespace Cuemon { public class StringDecoratorExtensionsTest : Test { - public StringDecoratorExtensionsTest(ITestOutputHelper output = null) : base(output) + public StringDecoratorExtensionsTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs index 697f88a26..c1bd35541 100644 --- a/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs @@ -21,7 +21,7 @@ namespace Cuemon { public class TypeDecoratorExtensionsTest : Test { - public TypeDecoratorExtensionsTest(ITestOutputHelper output = null) : base(output) + public TypeDecoratorExtensionsTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Core.Tests/ValidatorTest.cs b/test/Cuemon.Core.Tests/ValidatorTest.cs index c9f196a5e..1f7d8c1c2 100644 --- a/test/Cuemon.Core.Tests/ValidatorTest.cs +++ b/test/Cuemon.Core.Tests/ValidatorTest.cs @@ -9,7 +9,7 @@ namespace Cuemon { public class ValidatorTest : Test { - public ValidatorTest(ITestOutputHelper output = null) : base(output) + public ValidatorTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Data.Tests/ConcurrentDsvDataReaderTest.cs b/test/Cuemon.Data.Tests/ConcurrentDsvDataReaderTest.cs index b44ae47f0..09f1779f7 100644 --- a/test/Cuemon.Data.Tests/ConcurrentDsvDataReaderTest.cs +++ b/test/Cuemon.Data.Tests/ConcurrentDsvDataReaderTest.cs @@ -11,7 +11,7 @@ namespace Cuemon.Data { public class ConcurrentDsvDataReaderTest : Test { - public ConcurrentDsvDataReaderTest(ITestOutputHelper output = null) : base(output) + public ConcurrentDsvDataReaderTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Data.Tests/DsvDataReaderTest.cs b/test/Cuemon.Data.Tests/DsvDataReaderTest.cs index eab09eda7..eb87f4962 100644 --- a/test/Cuemon.Data.Tests/DsvDataReaderTest.cs +++ b/test/Cuemon.Data.Tests/DsvDataReaderTest.cs @@ -10,7 +10,7 @@ namespace Cuemon.Data { public class DsvDataReaderTest : Test { - public DsvDataReaderTest(ITestOutputHelper output = null) : base(output) + public DsvDataReaderTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Data.Tests/ExceptionTest.cs b/test/Cuemon.Data.Tests/ExceptionTest.cs index 5acfe099a..7388851ad 100644 --- a/test/Cuemon.Data.Tests/ExceptionTest.cs +++ b/test/Cuemon.Data.Tests/ExceptionTest.cs @@ -8,7 +8,7 @@ namespace Cuemon.Data { public class ExceptionTest : Test { - public ExceptionTest(ITestOutputHelper output = null) : base(output) + public ExceptionTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Extensions.Core.Tests/ValidatorExtensionsTest.cs b/test/Cuemon.Extensions.Core.Tests/ValidatorExtensionsTest.cs index 6faadff8c..4fb3c35dc 100644 --- a/test/Cuemon.Extensions.Core.Tests/ValidatorExtensionsTest.cs +++ b/test/Cuemon.Extensions.Core.Tests/ValidatorExtensionsTest.cs @@ -7,7 +7,7 @@ namespace Cuemon.Extensions { public class ValidatorExtensionsTest : Test { - public ValidatorExtensionsTest(ITestOutputHelper output = null) : base(output) + public ValidatorExtensionsTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Net.Tests/QueryStringCollectionTest.cs b/test/Cuemon.Net.Tests/QueryStringCollectionTest.cs index dc10f2ac1..63f2b2248 100644 --- a/test/Cuemon.Net.Tests/QueryStringCollectionTest.cs +++ b/test/Cuemon.Net.Tests/QueryStringCollectionTest.cs @@ -9,7 +9,7 @@ namespace Cuemon.Net { public class QueryStringCollectionTest : Test { - public QueryStringCollectionTest(ITestOutputHelper output = null) : base(output) + public QueryStringCollectionTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Resilience.Tests/ExceptionTest.cs b/test/Cuemon.Resilience.Tests/ExceptionTest.cs index 2175e606b..1963a91cd 100644 --- a/test/Cuemon.Resilience.Tests/ExceptionTest.cs +++ b/test/Cuemon.Resilience.Tests/ExceptionTest.cs @@ -11,7 +11,7 @@ namespace Cuemon.Resilience { public class ExceptionTest : Test { - public ExceptionTest(ITestOutputHelper output = null) : base(output) + public ExceptionTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Resilience.Tests/TransientOperationTest.cs b/test/Cuemon.Resilience.Tests/TransientOperationTest.cs index 51492acb3..7c0fd6250 100644 --- a/test/Cuemon.Resilience.Tests/TransientOperationTest.cs +++ b/test/Cuemon.Resilience.Tests/TransientOperationTest.cs @@ -24,7 +24,7 @@ public class TransientOperationTest : Test private static readonly TimeSpan ExpectedRecoveryWaitTime = TimeSpan.FromSeconds(1); private static readonly TimeSpan ExpectedMaximumAllowedLatency = TimeSpan.FromMilliseconds(250); - public TransientOperationTest(ITestOutputHelper output = null) : base(output) + public TransientOperationTest(ITestOutputHelper output) : base(output) { TransientOperation.FaultCallback = evidence => RetryTrackerCallback(evidence, _transientFaultTracker); TransientOperationOptionsCallback = o => diff --git a/test/Cuemon.Security.Cryptography.Tests/AesCryptorTest.cs b/test/Cuemon.Security.Cryptography.Tests/AesCryptorTest.cs index 2c576aebd..116cac04e 100644 --- a/test/Cuemon.Security.Cryptography.Tests/AesCryptorTest.cs +++ b/test/Cuemon.Security.Cryptography.Tests/AesCryptorTest.cs @@ -11,7 +11,7 @@ public class AesCryptorTest : Test private readonly byte[] _secretKey; private readonly byte[] _iv; - public AesCryptorTest(ITestOutputHelper output = null) : base(output) + public AesCryptorTest(ITestOutputHelper output) : base(output) { _secretKey = AesCryptor.GenerateKey(); _iv = AesCryptor.GenerateInitializationVector(); diff --git a/test/Cuemon.Security.Cryptography.Tests/KeyedHashFactoryTest.cs b/test/Cuemon.Security.Cryptography.Tests/KeyedHashFactoryTest.cs index f77524dd0..0f6177bab 100644 --- a/test/Cuemon.Security.Cryptography.Tests/KeyedHashFactoryTest.cs +++ b/test/Cuemon.Security.Cryptography.Tests/KeyedHashFactoryTest.cs @@ -6,7 +6,7 @@ namespace Cuemon.Security.Cryptography { public class KeyedHashFactoryTest : Test { - public KeyedHashFactoryTest(ITestOutputHelper output = null) : base(output) + public KeyedHashFactoryTest(ITestOutputHelper output) : base(output) { } diff --git a/test/Cuemon.Security.Cryptography.Tests/UnkeyedHashFactoryTest.cs b/test/Cuemon.Security.Cryptography.Tests/UnkeyedHashFactoryTest.cs index dd318c37a..75e8396f3 100644 --- a/test/Cuemon.Security.Cryptography.Tests/UnkeyedHashFactoryTest.cs +++ b/test/Cuemon.Security.Cryptography.Tests/UnkeyedHashFactoryTest.cs @@ -7,7 +7,7 @@ namespace Cuemon.Security.Cryptography { public class UnkeyedHashFactoryTest : Test { - public UnkeyedHashFactoryTest(ITestOutputHelper output = null) : base(output) + public UnkeyedHashFactoryTest(ITestOutputHelper output) : base(output) { } From 4ad55d30f0fb61d43768b3cac538810abf66ee3d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 00:35:30 +0200 Subject: [PATCH 151/385] Added unit test for XmlDataReader. --- .../Cuemon.Data.Tests/Assets/Professional.xml | 376 ++++++++++++++++++ .../Cuemon.Data.Tests.csproj | 4 + .../Xml/XmlDataReaderTest.cs | 75 ++++ 3 files changed, 455 insertions(+) create mode 100644 test/Cuemon.Data.Tests/Assets/Professional.xml create mode 100644 test/Cuemon.Data.Tests/Xml/XmlDataReaderTest.cs diff --git a/test/Cuemon.Data.Tests/Assets/Professional.xml b/test/Cuemon.Data.Tests/Assets/Professional.xml new file mode 100644 index 000000000..c6f1c3d78 --- /dev/null +++ b/test/Cuemon.Data.Tests/Assets/Professional.xml @@ -0,0 +1,376 @@ + + + + + 2 + 2 + + + 2 + 2 + + + 1 + 7 + 1 + + + 1 + 7 + 1 + + + 2 + 2 + 2 + 2 + 1 + 0 + 0 + 0 + + + 2 + 2 + 2 + 2 + 1 + 0 + 0 + 0 + + + 1 + + + 1 + + + 524288 + + + 65536 + + + WinNT + Terminal&#x20;Server + + + WinNT + Terminal&#x20;Server + + + %SystemRoot%\system32\BcastDVRBroker.dll + + + %SystemRoot%\system32\BcastDVRBroker.dll + + + 2 + 20 + + + 2 + 20 + + + 2 + 1 + + + 2 + 1 + + + + + + + + + + + + 1 + 100 + 3 + 3 + 0 + 0 + 2 + 1 + 1 + 1200 + 600 + 10 + 10 + 0 + 0 + 50000 + 50000 + 20 + 30 + 1 + 1 + 60 + 30 + 30 + 1 + 1 + 1800 + 900 + 0 + 0 + 120 + 120 + 1 + 70 + 0 + 20 + 1 + 900 + 5 + 300 + 1200 + 1200 + 1200 + 75 + 2 + 2 + 1 + 1 + 1200 + 1200 + 20 + 20 + 7 + 7 + 10 + 10 + 1 + 1 + 1 + 30 + 30 + 2 + 15 + 15 + 1 + 1 + 0 + 0 + 120 + 120 + 70 + 1 + 2 + 2 + 1 + 1 + 2 + 2 + 0 + 0 + 50000 + 50000 + 60 + 60 + 2 + 2 + 1 + 1 + 200 + 200 + 100 + 100 + 1 + 1 + 900 + 600 + 0 + 0 + 120 + 120 + 70 + 0 + 20 + 1 + O:BAG:SYD:P(A;CI;KRKW;;;BU)(A;CI;KA;;;BA)(A;CI;KA;;;SY)(A;CI;KA;;;CO)(A;CI;KR;;;AC)(A;CI;KR;;;S-1-15-3-1024-1502825166-1963708345-2616377461-2562897074-4192028372-3968301570-1997628692-1435953622) + 1 + 0 + 1 + 300 + 120 + 99 + 99 + 99 + 99 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 2 + 2 + 2 + 2 + 2 + 2 + 97 + 97 + 1 + 1 + 1 + 1 + 1 + 1 + + + 2 + 1 + 1 + 1200 + 600 + 10 + 10 + 0 + 0 + 50000 + 50000 + 20 + 30 + 1 + 1 + 60 + 30 + 30 + 1 + 1 + 1800 + 900 + 0 + 0 + 120 + 120 + 1 + 70 + 0 + 20 + 1 + 900 + 5 + 300 + 1200 + 1200 + 1200 + 75 + 2 + 2 + 1 + 1 + 1200 + 1200 + 20 + 20 + 7 + 7 + 10 + 10 + 1 + 1 + 1 + 30 + 30 + 2 + 15 + 15 + 1 + 1 + 0 + 0 + 120 + 120 + 70 + 1 + 2 + 2 + 1 + 1 + 2 + 2 + 0 + 0 + 50000 + 50000 + 60 + 60 + 2 + 2 + 1 + 1 + 200 + 200 + 100 + 100 + 1 + 1 + 900 + 600 + 0 + 0 + 120 + 120 + 70 + 0 + 20 + 1 + O:BAG:SYD:P(A;CI;KRKW;;;BU)(A;CI;KA;;;BA)(A;CI;KA;;;SY)(A;CI;KA;;;CO)(A;CI;KR;;;AC)(A;CI;KR;;;S-1-15-3-1024-1502825166-1963708345-2616377461-2562897074-4192028372-3968301570-1997628692-1435953622) + 1 + 0 + 1 + 300 + 120 + 99 + 99 + 99 + 99 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 2 + 2 + 2 + 2 + 2 + 2 + 97 + 97 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 100 + 3 + 3 + 0 + 0 + + + 0 + + + 0 + + + \ No newline at end of file diff --git a/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj b/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj index 982a39029..636f5b47c 100644 --- a/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj +++ b/test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj @@ -6,14 +6,18 @@ + + + + \ No newline at end of file diff --git a/test/Cuemon.Data.Tests/Xml/XmlDataReaderTest.cs b/test/Cuemon.Data.Tests/Xml/XmlDataReaderTest.cs new file mode 100644 index 000000000..b885da882 --- /dev/null +++ b/test/Cuemon.Data.Tests/Xml/XmlDataReaderTest.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.XPath; +using Cuemon.Extensions.Reflection; +using Cuemon.Extensions.Xunit; +using Cuemon.IO; +using Cuemon.Reflection; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Data.Xml +{ + public class XmlDataReaderTest : Test + { + public XmlDataReaderTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void XmlDataReader_ShouldReadAllRows() + { + var file = typeof(XmlDataReaderTest).GetEmbeddedResources("Professional.xml", ManifestResourceMatch.ContainsName).Values.Single(); + var msXml = new MemoryStream(); + Decorator.Enclose(file).CopyStream(msXml); + var xp = new XPathDocument(msXml).CreateNavigator(); + var xmlReader = xp.ReadSubtree(); + var sb1 = new StringBuilder(); + var sb2 = new StringBuilder(); + + string elementName = null; + while (xmlReader.Read()) + { + if (xmlReader.NodeType == XmlNodeType.Element) + { + elementName = xmlReader.LocalName; + } + + if (xmlReader.HasAttributes) + { + while (xmlReader.MoveToNextAttribute()) + { + sb1.AppendLine($"{xmlReader.LocalName}={xmlReader.Value}"); + } + + } + else if (!string.IsNullOrEmpty(xmlReader.Value)) + { + sb1.AppendLine($"{elementName}={xmlReader.Value}"); + } + } + + + XmlDataReader dataReader; + using (dataReader = new XmlDataReader(XmlReader.Create(file))) + { + while (dataReader.Read()) + { + for (var i = 0; i < dataReader.FieldCount; i++) + { + sb2.AppendLine($"{dataReader.GetName(i)}={dataReader.GetValue(i)}"); + } + } + Assert.True(xmlReader.EOF); + } + + Assert.Equal(sb1.ToString(), sb2.ToString()); + Assert.Equal(344, dataReader.RowCount); + Assert.True(dataReader.Disposed); + Assert.Throws(() => dataReader.Read()); + } + } +} \ No newline at end of file From b69cffba2e17c7aaaee6dba71b460f6ce532f733 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 18:48:55 +0200 Subject: [PATCH 152/385] Phrasing --- src/Cuemon.Core/Properties/PackageReleaseNotes.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt index ea0ae80e5..1fe4f8375 100644 --- a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt @@ -6,8 +6,8 @@ Availability: NET Standard 2.0 - # Breaking Changes -- REMOVED StringFormatter class in the Cuemon namespace -- REMOVED StandardizedDateTimeFormatPattern enum in the Cuemon namespace +- REMOVED StringFormatter class from the Cuemon namespace +- REMOVED StandardizedDateTimeFormatPattern enum from the Cuemon namespace - MOVED AsyncOptions class in the Cuemon.Threading namespace to its own assembly (by the same name and namespace) # New Features From a0b7c65022a324556ed8c80f0b355c49140876c3 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 18:49:16 +0200 Subject: [PATCH 153/385] Moved from Cuemon.Xml. --- src/Cuemon.Extensions.Xml/XmlCopyOptions.cs | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/Cuemon.Extensions.Xml/XmlCopyOptions.cs diff --git a/src/Cuemon.Extensions.Xml/XmlCopyOptions.cs b/src/Cuemon.Extensions.Xml/XmlCopyOptions.cs new file mode 100644 index 000000000..135a37593 --- /dev/null +++ b/src/Cuemon.Extensions.Xml/XmlCopyOptions.cs @@ -0,0 +1,25 @@ +using System; +using System.Xml; + +namespace Cuemon.Extensions.Xml +{ + /// + /// Configuration options for . + /// + public class XmlCopyOptions : DisposableOptions + { + /// + /// Initializes a new instance of the class. + /// + public XmlCopyOptions() + { + WriterSettings = null; + } + + /// + /// Gets or sets the which will be applied doing the copying process and need to be configured. + /// + /// The writer settings. + public Action WriterSettings { get; set; } + } +} \ No newline at end of file From 8db61e9b95a1b568a3e045a4bb05c10befd02d57 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 18:49:36 +0200 Subject: [PATCH 154/385] Consistent naming. --- src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs b/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs index 666f2ce57..b5580be00 100644 --- a/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs +++ b/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs @@ -5,7 +5,7 @@ namespace Cuemon.Xml.Serialization.Formatters { /// - /// Specifies options that is related to operations. + /// Configuration options for . /// public class XmlFormatterOptions { From 9732b0dbcfd261c1fd8b6038e657048ef8f938a6 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 18:49:58 +0200 Subject: [PATCH 155/385] Added support for WhiteSource Bolt scanning. --- azure-pipelines.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4c6dd119f..ed603b7c4 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -157,6 +157,10 @@ jobs: packagesToPack: src/**/*.csproj nobuild: true + - task: WhiteSource Bolt@20 + condition: eq(variables['Agent.OS'], 'Windows_NT') + displayName: 'WhiteSource Bolt' + - task: PublishBuildArtifacts@1 condition: eq(variables['Agent.OS'], 'Windows_NT') displayName: 'Store NuGet Packages' From ae59d63d2b0e95d45096a33b7eed8b504341e970 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 18:52:04 +0200 Subject: [PATCH 156/385] Added package release notes. --- .../Properties/PackageReleaseNotes.txt | 50 +++++++++++++++++++ src/Cuemon.Xml/XmlCopyOptions.cs | 25 ---------- 2 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 src/Cuemon.Xml/Properties/PackageReleaseNotes.txt delete mode 100644 src/Cuemon.Xml/XmlCopyOptions.cs diff --git a/src/Cuemon.Xml/Properties/PackageReleaseNotes.txt b/src/Cuemon.Xml/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..6da38bf73 --- /dev/null +++ b/src/Cuemon.Xml/Properties/PackageReleaseNotes.txt @@ -0,0 +1,50 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# Upgrade Steps +- The Cuemon.Serialization.Xml namespace was removed with this version +- Any XML serialization found in the Cuemon.Serialization.Xml namespace was merged into the Cuemon.Xml.Serialization namespace +- Any former extension methods of the Cuemon.Xml namespace was merged into the Cuemon.Extensions.Xml namespace +- The extent of refactoring applied to this project resulted in so many breaking changes that a git diff is advisable +  +# Breaking Changes +- REMOVED XElementExtensions class from the Cuemon.Xml.Serialization.Linq namespace +- REMOVED SerializableOrder class from the Cuemon.Xml.Serialization namespace +- REMOVED XmlJsonInstance class from the Cuemon.Xml.Serialization namespace +- RENAMED XPathNavigableConverter class in the Cuemon.Xml.XPath namespace to XPathDocumentFactory (including rename of remaining members) +- REMOVED JsonStreamConverter class from the Cuemon.Xml namespace +- REMOVED SecureXmlObfuscator class from the Cuemon.Xml namespace +- REMOVED XmlConvertExtensions class from the Cuemon.Xml namespace +- REMOVED XmlCopyOptions class from the Cuemon.Xml namespace +- RENAMED XmlDocumentConverter class in the Cuemon.Xml namespace to XmlDocumentFactory (including rename of remaining members) +- REMOVED XmlEncodingUtility class from the Cuemon.Xml namespace +- REMOVED XmlObfuscator class from the Cuemon.Xml namespace +- REMOVED XmlReaderConverter class from the Cuemon.Xml namespace +- REMOVED XmlReaderUtility class from the Cuemon.Xml namespace +- REMOVED XmlReaderUtilityExtensions class from the Cuemon.Xml namespace +- RENAMED XmlStreamConverter class in the Cuemon.Xml namespace to XmlStreamFactory (including rename of remaining members) +- REMOVED XmlUtility class from the Cuemon.Xml namespace +- REMOVED XmlUtilityExtensions class from the Cuemon.Xml namespace +- REMOVED XmlWriterUtility class from Cuemon.Xml namespace +- REMOVED XmlWriterUtilityExtensions class from the Cuemon.Xml namespace +  +# New Features +- MERGED DefaultXmlConverter class into Cuemon.Xml.Serialization.Converters namespace that provides a default way to convert objects to and from XML +- MERGED XmlConverter class into Cuemon.Xml.Serialization.Converters namespace that converts an object to and from XML +- MERGED XmlFormatter class into Cuemon.Xml.Serialization.Formatters namespace that serializes and deserializes an object in XML format +- MERGED XmlFormatterOptions class into Cuemon.Xml.Serialization.Formatters namespace that specifies configuration options for XmlFormatter +- MERGED DynamicXmlConverter class into Cuemon.Xml.Serialization namespace that provides a factory based way to create and wrap an XmlConverter implementation +- MERGED DynamicXmlSerializable class into Cuemon.Xml.Serialization namespace that provides a factory based way to create and wrap an IXmlSerializable implementation +- MERGED XmlConvert class into Cuemon.Xml.Serialization namespace that provides methods for converting between .NET types and XML types +- MERGED XmlSerializer class into Cuemon.Xml.Serialization namespace that serializes and deserializes objects into and from the XML format +- MERGED XmlSerializerSettings class into Cuemon.Xml.Serialization namespace that specifies configuration options for XmlSerializer +  +# Improvements +- EXTENDED XmlSerializer class in the Cuemon.Xml.Serialization namespace with a new overloaded method: Serialize +- RENAMED XmlSerializerSettings class in the Cuemon.Xml.Serialization namespace to XmlSerializerOptions +  +# Bug Fixes +- FIXED bug that in some cases could throw a SerializationException when serializable type consisted of only a default contructor (Cuemon.Xml.Serialization.Converters.DefaultXmlConverter.ParseReadXmlDefault) +- FIXED bug that would trigger a redundant entry of InnerException on AggregateException when serializing exceptions (Cuemon.Xml.Serialization.Converters.XmlConverterDecoratorExtensions.WriteInnerExceptions) +- FIXED bug that could trigger a NullReferenceException when serializing namespace information on an Exception class (Cuemon.Xml.Serialization.Converters.XmlConverterDecoratorExtensions.WriteException) +- FIXED assignment of default converters with multicast delegate (Cuemon.Xml.Serialization.Formatters.XmlFormatterOptions.ctor) \ No newline at end of file diff --git a/src/Cuemon.Xml/XmlCopyOptions.cs b/src/Cuemon.Xml/XmlCopyOptions.cs deleted file mode 100644 index 27fbcf933..000000000 --- a/src/Cuemon.Xml/XmlCopyOptions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Xml; - -namespace Cuemon.Xml -{ - /// - /// Configuration options for . - /// - public class XmlCopyOptions : DisposableOptions - { - /// - /// Initializes a new instance of the class. - /// - public XmlCopyOptions() - { - WriterSettings = null; - } - - /// - /// Gets or sets the which will be applied doing the copying process and need to be configured. - /// - /// The writer settings. - public Action WriterSettings { get; set; } - } -} \ No newline at end of file From 3a0e262f62e67daf41eaaaec85ae3f1e5ecdc618 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 21:21:16 +0200 Subject: [PATCH 157/385] AutoFixture (maybe because i don't understand CreateMany) caused errors; changed to Generate.RangeOf(). --- .../DictionaryDecoratorExtensionsTest.cs | 31 ++++++------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/test/Cuemon.Core.Tests/Collections/Generic/DictionaryDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Collections/Generic/DictionaryDecoratorExtensionsTest.cs index 0db4ab300..5fd19873b 100644 --- a/test/Cuemon.Core.Tests/Collections/Generic/DictionaryDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Collections/Generic/DictionaryDecoratorExtensionsTest.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using AutoFixture; using Xunit; namespace Cuemon.Collections.Generic @@ -10,8 +9,7 @@ public class DictionaryDecoratorExtensionsTest [Fact] public void Extend_Dictionary_With_GetValueOrDefault_Expect_Actual_Value_At_Key_42() { - var fixture = new Fixture(); - var dic = fixture.CreateMany>(500).ToDictionary(x => x.Key, x => x.Value); + var dic = Generate.RangeOf(500, x => new KeyValuePair(x, Generate.RandomString(24))).ToDictionary(x => x.Key, x => x.Value); var result = Decorator.Enclose(dic).GetValueOrDefault(42); Assert.Equal(dic[42], result); } @@ -19,8 +17,7 @@ public void Extend_Dictionary_With_GetValueOrDefault_Expect_Actual_Value_At_Key_ [Fact] public void Extend_Dictionary_With_GetValueOrDefault_Expect_Default_Provided_Value() { - var fixture = new Fixture(); - var dic = fixture.CreateMany>(500).ToDictionary(x => x.Key, x => x.Value); + var dic = Generate.RangeOf(500, x => new KeyValuePair(x, Generate.RandomString(24))).ToDictionary(x => x.Key, x => x.Value); var expected = Generate.RandomString(24); var result = Decorator.Enclose(dic).GetValueOrDefault(700, () => expected); Assert.Equal(expected, result); @@ -29,8 +26,7 @@ public void Extend_Dictionary_With_GetValueOrDefault_Expect_Default_Provided_Val [Fact] public void Extend_Dictionary_With_TryGetValueOrFallback_Expect_Actual_Value_At_Key_42() { - var fixture = new Fixture(); - var dic = fixture.CreateMany>(500).ToDictionary(x => x.Key, x => x.Value); + var dic = Generate.RangeOf(500, x => new KeyValuePair(x, Generate.RandomString(24))).ToDictionary(x => x.Key, x => x.Value); var found = Decorator.Enclose(dic).TryGetValueOrFallback(42, keys => keys.Max(), out var result); Assert.True(found); Assert.Equal(dic[42], result); @@ -39,8 +35,7 @@ public void Extend_Dictionary_With_TryGetValueOrFallback_Expect_Actual_Value_At_ [Fact] public void Extend_Dictionary_With_TryGetValueOrFallback_Expect_Fallback_Value_At_Key_42() { - var fixture = new Fixture(); - var dic = fixture.CreateMany>(500).ToDictionary(x => x.Key, x => x.Value); + var dic = Generate.RangeOf(500, x => new KeyValuePair(x, Generate.RandomString(24))).ToDictionary(x => x.Key, x => x.Value); var found = Decorator.Enclose(dic).TryGetValueOrFallback(-1, keys => 42, out var result); Assert.True(found); Assert.Equal(dic[42], result); @@ -49,8 +44,7 @@ public void Extend_Dictionary_With_TryGetValueOrFallback_Expect_Fallback_Value_A [Fact] public void Extend_Dictionary_With_TryGetValueOrFallback_Expect_Not_Found() { - var fixture = new Fixture(); - var dic = fixture.CreateMany>(500).ToDictionary(x => x.Key, x => x.Value); + var dic = Generate.RangeOf(500, x => new KeyValuePair(x, Generate.RandomString(24))).ToDictionary(x => x.Key, x => x.Value); var found = Decorator.Enclose(dic).TryGetValueOrFallback(-1, keys => -42, out var result); Assert.False(found); Assert.Equal(default, result); @@ -59,8 +53,7 @@ public void Extend_Dictionary_With_TryGetValueOrFallback_Expect_Not_Found() [Fact] public void Extend_Dictionary_With_ToEnumerable_Expect_Sequence() { - var fixture = new Fixture(); - var dic = fixture.CreateMany>(500).ToDictionary(x => x.Key, x => x.Value); + var dic = Generate.RangeOf(500, x => new KeyValuePair(x, Generate.RandomString(24))).ToDictionary(x => x.Key, x => x.Value); var result = Decorator.Enclose(dic).ToEnumerable(); Assert.Equal(dic, result); Assert.IsAssignableFrom>>(result); @@ -69,8 +62,7 @@ public void Extend_Dictionary_With_ToEnumerable_Expect_Sequence() [Fact] public void Extend_Dictionary_With_TryAdd_Expect_True() { - var fixture = new Fixture(); - var dic = fixture.CreateMany>(500).ToDictionary(x => x.Key, x => x.Value); + var dic = Generate.RangeOf(500, x => new KeyValuePair(x, Generate.RandomString(24))).ToDictionary(x => x.Key, x => x.Value); Assert.Equal(500, dic.Count); var added = Decorator.Enclose(dic).TryAdd(501, "First Legion"); Assert.True(added); @@ -80,8 +72,7 @@ public void Extend_Dictionary_With_TryAdd_Expect_True() [Fact] public void Extend_Dictionary_With_TryAdd_Expect_False() { - var fixture = new Fixture(); - var dic = fixture.CreateMany>(500).ToDictionary(x => x.Key, x => x.Value); + var dic = Generate.RangeOf(500, x => new KeyValuePair(x, Generate.RandomString(24))).ToDictionary(x => x.Key, x => x.Value); var key = dic.Keys.Last(); Assert.Equal(500, dic.Count); var added = Decorator.Enclose(dic).TryAdd(key, "First Legion"); @@ -92,8 +83,7 @@ public void Extend_Dictionary_With_TryAdd_Expect_False() [Fact] public void Extend_Dictionary_With_TryAddOrUpdate_Add_And_Update_Expect_True() { - var fixture = new Fixture(); - var dic = fixture.CreateMany>(500).ToDictionary(x => x.Key, x => x.Value); + var dic = Generate.RangeOf(500, x => new KeyValuePair(x, Generate.RandomString(24))).ToDictionary(x => x.Key, x => x.Value); var key = 501; var txt = "First Legion"; var subtxt = " Rules the Galaxy"; @@ -109,8 +99,7 @@ public void Extend_Dictionary_With_TryAddOrUpdate_Add_And_Update_Expect_True() [Fact] public void Extend_Dictionary_With_TryAddOrUpdate_Update_Expect_True() { - var fixture = new Fixture(); - var dic = fixture.CreateMany>(500).ToDictionary(x => x.Key, x => x.Value); + var dic = Generate.RangeOf(500, x => new KeyValuePair(x, Generate.RandomString(24))).ToDictionary(x => x.Key, x => x.Value); var elm = dic.Last(); var txt = "First Legion"; Assert.Equal(500, dic.Count); From bdd7d4fca64ea6f83752504a3836dadf0ab74173 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 8 Sep 2020 22:16:24 +0200 Subject: [PATCH 158/385] Exclude test projects from WhiteSource. --- azure-pipelines.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ed603b7c4..eb8bed3ed 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -160,6 +160,9 @@ jobs: - task: WhiteSource Bolt@20 condition: eq(variables['Agent.OS'], 'Windows_NT') displayName: 'WhiteSource Bolt' + inputs: + advance: true + exclude: 'test' - task: PublishBuildArtifacts@1 condition: eq(variables['Agent.OS'], 'Windows_NT') From e4508d09d85eed57880d2f7e7eb567b784f63b32 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 9 Sep 2020 03:03:14 +0200 Subject: [PATCH 159/385] Throws TaskCancelledException if cancellation is requested. --- src/Cuemon.Core/TaskActionFactory.cs | 1 + src/Cuemon.Core/TaskFuncFactory.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Cuemon.Core/TaskActionFactory.cs b/src/Cuemon.Core/TaskActionFactory.cs index 302135a45..d479d756f 100644 --- a/src/Cuemon.Core/TaskActionFactory.cs +++ b/src/Cuemon.Core/TaskActionFactory.cs @@ -451,6 +451,7 @@ internal TaskActionFactory(Func method, TTuple public Task ExecuteMethodAsync(CancellationToken ct) { ThrowIfNoValidDelegate(Condition.IsNull(Method)); + if (ct.IsCancellationRequested) { throw new TaskCanceledException(); } return Method.Invoke(GenericArguments, ct); } diff --git a/src/Cuemon.Core/TaskFuncFactory.cs b/src/Cuemon.Core/TaskFuncFactory.cs index 70ac1166f..bdee522e0 100644 --- a/src/Cuemon.Core/TaskFuncFactory.cs +++ b/src/Cuemon.Core/TaskFuncFactory.cs @@ -468,6 +468,7 @@ internal TaskFuncFactory(Func> method, public Task ExecuteMethodAsync(CancellationToken ct) { ThrowIfNoValidDelegate(Condition.IsNull(Method)); + if (ct.IsCancellationRequested) { throw new TaskCanceledException(); } return Method.Invoke(GenericArguments, ct); } From fdd3eff5ec101988c32f18ba499063c78e34acff Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 9 Sep 2020 03:03:29 +0200 Subject: [PATCH 160/385] Phrasing. --- .../TransientOperation.Async.cs | 12 +- .../AdvancedParallelFactory.For.cs | 230 ++++++++++++++++ .../AdvancedParallelFactory.ForAsync.cs | 203 ++++++++++++++ .../AdvancedParallelFactory.ForResultAsync.cs | 237 ++++++++++++++++ ... => AdvancedParallelFactory.WhileAsync.cs} | 31 +-- ...vancedParallelFactory.WhileResultAsync.cs} | 16 +- .../AdvancedParallelFactory.cs | 54 ++++ ...yOptions.cs => AsyncTaskFactoryOptions.cs} | 19 +- src/Cuemon.Threading/AsyncWorkloadOptions.cs | 37 +++ src/Cuemon.Threading/ParallelFactory.For.cs | 236 ++++++++++++++++ .../ParallelFactory.ForAsync.cs | 254 ++++-------------- .../ParallelFactory.ForEachAsync.cs | 26 +- .../ParallelFactory.ForEachResultAsync.cs | 26 +- .../ParallelFactory.ForResultAsync.cs | 193 +++---------- src/Cuemon.Threading/ParallelFactory.cs | 9 + .../Properties/PackageReleaseNotes.txt | 4 + test/Cuemon.Threading.Tests/ForAsyncTest.cs | 29 -- .../ForEachAsyncTest.cs | 1 - .../ParallelFactoryAsyncTest.cs | 94 +++++++ .../ParallelFactoryTest.cs | 104 +++++++ test/Cuemon.Threading.Tests/WhileAsyncTest.cs | 4 +- .../WhileResultAsyncTest.cs | 2 +- 22 files changed, 1368 insertions(+), 453 deletions(-) create mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.For.cs create mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs create mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs rename src/Cuemon.Threading/{ParallelFactory.WhileAsync.cs => AdvancedParallelFactory.WhileAsync.cs} (91%) rename src/Cuemon.Threading/{ParallelFactory.WhileResultAsync.cs => AdvancedParallelFactory.WhileResultAsync.cs} (97%) create mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.cs rename src/Cuemon.Threading/{TaskFactoryOptions.cs => AsyncTaskFactoryOptions.cs} (72%) create mode 100644 src/Cuemon.Threading/AsyncWorkloadOptions.cs create mode 100644 src/Cuemon.Threading/ParallelFactory.For.cs create mode 100644 src/Cuemon.Threading/ParallelFactory.cs delete mode 100644 test/Cuemon.Threading.Tests/ForAsyncTest.cs create mode 100644 test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs create mode 100644 test/Cuemon.Threading.Tests/ParallelFactoryTest.cs diff --git a/src/Cuemon.Resilience/TransientOperation.Async.cs b/src/Cuemon.Resilience/TransientOperation.Async.cs index f1349566d..eb5856ac6 100644 --- a/src/Cuemon.Resilience/TransientOperation.Async.cs +++ b/src/Cuemon.Resilience/TransientOperation.Async.cs @@ -10,7 +10,7 @@ public static partial class TransientOperation /// Repetitively executes the specified until the operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. ///
/// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The token to monitor for cancellation requests. The default value is . /// The which may be configured. /// The result from the . @@ -40,7 +40,7 @@ public static Task WithFuncAsync(Func /// The type of the parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The parameter of the function delegate . /// The token to monitor for cancellation requests. The default value is . /// The which may be configured. @@ -72,7 +72,7 @@ public static Task WithFuncAsync(FuncThe type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The token to monitor for cancellation requests. The default value is . @@ -106,7 +106,7 @@ public static Task WithFuncAsync(FuncThe type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . @@ -142,7 +142,7 @@ public static Task WithFuncAsync(FuncThe type of the third parameter of the function delegate . /// The type of the fourth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . @@ -180,7 +180,7 @@ public static Task WithFuncAsync(FuncThe type of the fourth parameter of the function delegate . /// The type of the fifth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.For.cs b/src/Cuemon.Threading/AdvancedParallelFactory.For.cs new file mode 100644 index 000000000..5a720cb39 --- /dev/null +++ b/src/Cuemon.Threading/AdvancedParallelFactory.For.cs @@ -0,0 +1,230 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + public static partial class AdvancedParallelFactory + { + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the parameter of the delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The parameter of the delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T arg, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from, arg), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from, arg1, arg2), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from, arg1, arg2, arg3), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from, arg1, arg2, arg3, arg4), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The fifth parameter of the delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from, arg1, arg2, arg3, arg4, arg5), condition, iterator, setup); + } + + private static void ForCore(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, ActionFactory workerFactory, Func condition, Func iterator, Action setup) + where TWorker : Template + where TNumber : struct, IComparable, IEquatable, IConvertible + { + if (condition == null) { condition = Condition; } + if (iterator == null) { iterator = Iterator; } + + var options = Patterns.Configure(setup); + var exceptions = new ConcurrentBag(); + + TNumber processed = default; + while (true) + { + var workChunks = options.PartitionSize; + var queue = new List(); + for (var i = from; condition(i, relation, to); i = iterator(i, assignment, step)) + { + if (options.CancellationToken.IsCancellationRequested) + { + queue.Clear(); + break; + } + queue.Add(Task.Factory.StartNew(j => + { + var shallowWorkerFactory = workerFactory.Clone(); + try + { + shallowWorkerFactory.GenericArguments.Arg1 = (TNumber)j; + shallowWorkerFactory.ExecuteMethod(); + } + catch (Exception e) + { + exceptions.Add(e); + } + }, i, options.CancellationToken, options.CreationOptions, options.Scheduler)); + + processed = i; + workChunks--; + + if (workChunks == 0) { break; } + } + from = Calculator.Calculate(processed, assignment, step); + if (queue.Count == 0) { break; } + + if (options.CancellationToken.IsCancellationRequested) + { + queue.Clear(); + break; + } + + try + { + Task.WaitAll(queue.ToArray(), options.CancellationToken); + } + catch (OperationCanceledException oce) + { + exceptions.Add(oce); + } + + if (workChunks > 1) { break; } + } + if (exceptions.Count > 0) { throw new AggregateException(exceptions); } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs new file mode 100644 index 000000000..0425c93e3 --- /dev/null +++ b/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + public static partial class AdvancedParallelFactory + { + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The based function delegate that is invoked once per iteration. + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the parameter of the delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The based function delegate that is invoked once per iteration. + /// The parameter of the delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T arg, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from, arg), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The based function delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from, arg1, arg2), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The based function delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from, arg1, arg2, arg3), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The based function delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from, arg1, arg2, arg3, arg4), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The based function delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The fifth parameter of the delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from, arg1, arg2, arg3, arg4, arg5), condition, iterator, setup); + } + + private static async Task ForCoreAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, TaskActionFactory workerFactory, Func condition, Func iterator, Action setup) + where TWorker : Template + where TNumber : struct, IComparable, IEquatable, IConvertible + { + if (condition == null) { condition = Condition; } + if (iterator == null) { iterator = Iterator; } + + var options = Patterns.Configure(setup); + + TNumber processed = default; + while (true) + { + var workChunks = options.PartitionSize; + var queue = new List(); + for (var i = from; condition(i, relation, to); i = iterator(i, assignment, step)) + { + workerFactory.GenericArguments.Arg1 = i; + queue.Add(workerFactory.ExecuteMethodAsync(options.CancellationToken)); + + processed = i; + workChunks--; + + if (workChunks == 0) { break; } + } + from = Calculator.Calculate(processed, assignment, step); + if (queue.Count == 0) { break; } + await Task.WhenAll(queue).ConfigureAwait(false); + if (workChunks > 1) { break; } + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs new file mode 100644 index 000000000..5f0dd677a --- /dev/null +++ b/src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + public static partial class AdvancedParallelFactory + { + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the return value of the function delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCoreAsync(from, relation, to, assignment, step, FuncFactory.Create(worker, from), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The parameter of the function delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T arg, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + var wf = FuncFactory.Create(worker, from, arg); + return ForResultCoreAsync(from, relation, to, assignment, step, wf, condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCoreAsync(from, relation, to, assignment, step, FuncFactory.Create(worker, from, arg1, arg2), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCoreAsync(from, relation, to, assignment, step, FuncFactory.Create(worker, from, arg1, arg2, arg3), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCoreAsync(from, relation, to, assignment, step, FuncFactory.Create(worker, from, arg1, arg2, arg3, arg4), condition, iterator, setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the fifth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The fifth parameter of the function delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCoreAsync(from, relation, to, assignment, step, FuncFactory.Create(worker, from, arg1, arg2, arg3, arg4, arg5), condition, iterator, setup); + } + + private static async Task> ForResultCoreAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, FuncFactory workerFactory, Func condition, Func iterator, Action setup) + where TWorker : Template + where TNumber : struct, IComparable, IEquatable, IConvertible + { + if (condition == null) { condition = Condition; } + if (iterator == null) { iterator = Iterator; } + + var options = Patterns.Configure(setup); + var exceptions = new ConcurrentBag(); + var result = new ConcurrentDictionary(); + + while (true) + { + var workChunks = options.PartitionSize; + var queue = new List(); + for (var i = from; condition(i, relation, to); i = iterator(i, assignment, step)) + { + var shallowWorkerFactory = workerFactory.Clone(); + queue.Add(Task.Factory.StartNew(j => + { + try + { + var number = (TNumber)j; + shallowWorkerFactory.GenericArguments.Arg1 = number; + var presult = shallowWorkerFactory.ExecuteMethod(); + result.TryAdd(number, presult); + } + catch (Exception e) + { + exceptions.Add(e); + } + }, i, options.CancellationToken, options.CreationOptions, options.Scheduler)); + + workChunks--; + + if (workChunks == 0) + { + from = Calculator.Calculate(i, assignment, step); + break; + } + } + if (queue.Count == 0) { break; } + await Task.WhenAll(queue).ConfigureAwait(false); + if (workChunks > 1) { break; } + } + if (exceptions.Count > 0) { throw new AggregateException(exceptions); } + return new ReadOnlyCollection(result.Values.ToList()); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.WhileAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs similarity index 91% rename from src/Cuemon.Threading/ParallelFactory.WhileAsync.cs rename to src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs index 032cd47cb..6c137baec 100644 --- a/src/Cuemon.Threading/ParallelFactory.WhileAsync.cs +++ b/src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs @@ -6,10 +6,7 @@ namespace Cuemon.Threading { - /// - /// Provides a factory based way to encapsulate and re-use existing code while adding support for typically long-running parallel loops and regions. - /// - public static partial class ParallelFactory + public static partial class AdvancedParallelFactory { /// /// Executes a parallel while loop. @@ -20,9 +17,9 @@ public static partial class ParallelFactory /// The function delegate that is responsible for the while loop condition. /// The function delegate that provides data from the specified . /// The delegate that will perform work while evaluates true. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -42,9 +39,9 @@ public static Task WhileAsync(TReader reader, Func /// The function delegate that provides data from the specified . /// The delegate that will perform work while evaluates true. /// The parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T arg, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T arg, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -66,9 +63,9 @@ public static Task WhileAsync(TReader reader, FuncThe delegate that will perform work while evaluates true. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -92,9 +89,9 @@ public static Task WhileAsync(TReader reader, FuncThe first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -120,9 +117,9 @@ public static Task WhileAsync(TReader reader, Fun /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -150,9 +147,9 @@ public static Task WhileAsync(TReader reader, /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -161,7 +158,7 @@ public static Task WhileAsync(TReader rea return WhileCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); } - private static async Task WhileCoreAsync(ForwardIterator iterator, ActionFactory workerFactory, Action setup) + private static async Task WhileCoreAsync(ForwardIterator iterator, ActionFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); diff --git a/src/Cuemon.Threading/ParallelFactory.WhileResultAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs similarity index 97% rename from src/Cuemon.Threading/ParallelFactory.WhileResultAsync.cs rename to src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs index 0fdc25ea0..283caf170 100644 --- a/src/Cuemon.Threading/ParallelFactory.WhileResultAsync.cs +++ b/src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs @@ -8,7 +8,7 @@ namespace Cuemon.Threading { - public static partial class ParallelFactory + public static partial class AdvancedParallelFactory { /// /// Executes a parallel while loop where the return value of the function delegate is stored in the same order as the while loop evaluates true. @@ -23,7 +23,7 @@ public static partial class ParallelFactory /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -47,7 +47,7 @@ public static Task> WhileResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T arg, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T arg, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -73,7 +73,7 @@ public static Task> WhileResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -101,7 +101,7 @@ public static Task> WhileResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -131,7 +131,7 @@ public static Task> WhileResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -163,7 +163,7 @@ public static Task> WhileResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -172,7 +172,7 @@ public static Task> WhileResultAsync(reader, condition, provider), wf, setup); } - private static async Task> WhileResultCoreAsync(ForwardIterator iterator, FuncFactory workerFactory, Action setup) + private static async Task> WhileResultCoreAsync(ForwardIterator iterator, FuncFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.cs b/src/Cuemon.Threading/AdvancedParallelFactory.cs new file mode 100644 index 000000000..fb04f9e11 --- /dev/null +++ b/src/Cuemon.Threading/AdvancedParallelFactory.cs @@ -0,0 +1,54 @@ +using System; + +namespace Cuemon.Threading +{ + /// + /// Provides a factory based way to work with advanced scenarios that encapsulate and re-use existing code while adding support for typically long-running parallel loops and regions. + /// + public static partial class AdvancedParallelFactory + { + /// + /// Provides a default implementation of a for-iterator callback method. + /// + /// The type of the counter in a for-loop. + /// The current value of the counter in a for-loop. + /// One of the enumeration values that specifies the rules to apply as the assignment operator for left-hand operand and right-hand operand . + /// The value to assign to according to the rule specified by . + /// The computed result of having the of . + public static T Iterator(T current, AssignmentOperator assignment, T step) where T : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + return Calculator.Calculate(current, assignment, step); + } + + /// + /// Provides a default implementation of a for-condition callback method. + /// + /// The type of the counter in a for-loop. + /// The current value of the counter in a for-loop. + /// One of the enumeration values that specifies the rules to apply as the relational operator for left-hand operand and right-hand operand . + /// The amount of repeats to do according to the rules specified by . + /// true if does not meet the condition of and ; otherwise false. + public static bool Condition(T current, RelationalOperator relational, T repeats) where T : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + switch (relational) + { + case RelationalOperator.Equal: + return current.Equals(repeats); + case RelationalOperator.GreaterThan: + return current.CompareTo(repeats) > 0; + case RelationalOperator.GreaterThanOrEqual: + return current.CompareTo(repeats) >= 0; + case RelationalOperator.LessThan: + return current.CompareTo(repeats) < 0; + case RelationalOperator.LessThanOrEqual: + return current.CompareTo(repeats) <= 0; + case RelationalOperator.NotEqual: + return !current.Equals(repeats); + default: + throw new ArgumentOutOfRangeException(nameof(relational)); + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/TaskFactoryOptions.cs b/src/Cuemon.Threading/AsyncTaskFactoryOptions.cs similarity index 72% rename from src/Cuemon.Threading/TaskFactoryOptions.cs rename to src/Cuemon.Threading/AsyncTaskFactoryOptions.cs index cf0cbc0cf..5c42402f8 100644 --- a/src/Cuemon.Threading/TaskFactoryOptions.cs +++ b/src/Cuemon.Threading/AsyncTaskFactoryOptions.cs @@ -4,22 +4,22 @@ namespace Cuemon.Threading { /// - /// Configuration options for . + /// Configuration options for . /// - public class TaskFactoryOptions : AsyncOptions + public class AsyncTaskFactoryOptions : AsyncWorkloadOptions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// - /// The following table shows the initial property values for an instance of . + /// The following table shows the initial property values for an instance of . /// /// /// Property /// Initial Value /// /// - /// + /// /// 2 x /// /// @@ -32,19 +32,12 @@ public class TaskFactoryOptions : AsyncOptions /// /// /// - public TaskFactoryOptions() + public AsyncTaskFactoryOptions() { CreationOptions = TaskCreationOptions.LongRunning; Scheduler = TaskScheduler.Current; - PartitionSize = 2 * Environment.ProcessorCount; } - /// - /// Gets or sets the size of the partition to allocate work to a set of tasks. - /// - /// The size of the partition to allocate work to a set of tasks. - public int PartitionSize { get; set; } - /// /// Gets or sets the used to create the task. /// diff --git a/src/Cuemon.Threading/AsyncWorkloadOptions.cs b/src/Cuemon.Threading/AsyncWorkloadOptions.cs new file mode 100644 index 000000000..43781d81a --- /dev/null +++ b/src/Cuemon.Threading/AsyncWorkloadOptions.cs @@ -0,0 +1,37 @@ +using System; + +namespace Cuemon.Threading +{ + /// + /// Configuration options for . + /// + public class AsyncWorkloadOptions : AsyncOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// 2 x + /// + /// + /// + public AsyncWorkloadOptions() + { + PartitionSize = 2 * Environment.ProcessorCount; + } + + /// + /// Gets or sets the size of the partition to allocate work to a set of tasks. + /// + /// The size of the partition to allocate work to a set of tasks. + public int PartitionSize { get; set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.For.cs b/src/Cuemon.Threading/ParallelFactory.For.cs new file mode 100644 index 000000000..ababd29c6 --- /dev/null +++ b/src/Cuemon.Threading/ParallelFactory.For.cs @@ -0,0 +1,236 @@ +using System; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + public static partial class ParallelFactory + { + /// + /// Executes a parallel for loop. + /// + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(int fromInclusive, int toExclusive, Action worker, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(int fromInclusive, int toExclusive, Action worker, T arg, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The fifth parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(long fromInclusive, long toExclusive, Action worker, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(long fromInclusive, long toExclusive, Action worker, T arg, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The fifth parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.ForAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForAsync.cs index aef84e397..1b404de9d 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForAsync.cs @@ -1,68 +1,23 @@ using System; -using System.Collections.Concurrent; -using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Cuemon.Threading { public static partial class ParallelFactory { - /// - /// Provides a default implementation of a for-iterator callback method. - /// - /// The type of the counter in a for-loop. - /// The current value of the counter in a for-loop. - /// One of the enumeration values that specifies the rules to apply as the assignment operator for left-hand operand and right-hand operand . - /// The value to assign to according to the rule specified by . - /// The computed result of having the of . - public static T Iterator(T current, AssignmentOperator assignment, T step) where T : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - return Calculator.Calculate(current, assignment, step); - } - - /// - /// Provides a default implementation of a for-condition callback method. - /// - /// The type of the counter in a for-loop. - /// The current value of the counter in a for-loop. - /// One of the enumeration values that specifies the rules to apply as the relational operator for left-hand operand and right-hand operand . - /// The amount of repeats to do according to the rules specified by . - /// true if does not meet the condition of and ; otherwise false. - public static bool Condition(T current, RelationalOperator relational, T repeats) where T : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - switch (relational) - { - case RelationalOperator.Equal: - return current.Equals(repeats); - case RelationalOperator.GreaterThan: - return current.CompareTo(repeats) > 0; - case RelationalOperator.GreaterThanOrEqual: - return current.CompareTo(repeats) >= 0; - case RelationalOperator.LessThan: - return current.CompareTo(repeats) < 0; - case RelationalOperator.LessThanOrEqual: - return current.CompareTo(repeats) <= 0; - case RelationalOperator.NotEqual: - return !current.Equals(repeats); - default: - throw new ArgumentOutOfRangeException(nameof(relational)); - } - } - /// /// Executes a parallel for loop. /// /// The start index, inclusive. /// The end index, exclusive. /// The delegate that is invoked once per iteration. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); } /// @@ -73,12 +28,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, Action work /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T arg, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T arg, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); } /// @@ -91,12 +46,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, ActionThe delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); } /// @@ -111,12 +66,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, ActionThe first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); } /// @@ -133,12 +88,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, Acti /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); } /// @@ -157,225 +112,126 @@ public static Task ForAsync(int fromInclusive, int toExclusive, /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. /// The type of the parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T arg, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T arg, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . /// The type of the third parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . /// The type of the third parameter of the delegate . /// The type of the fourth parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . /// The type of the third parameter of the delegate . /// The type of the fourth parameter of the delegate . /// The type of the fifth parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); - } - - private static async Task ForCoreAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, ActionFactory workerFactory, Func condition, Func iterator, Action setup) - where TWorker : Template - where TNumber : struct, IComparable, IEquatable, IConvertible - { - if (condition == null) { condition = Condition; } - if (iterator == null) { iterator = Iterator; } - - var options = Patterns.Configure(setup); - var exceptions = new ConcurrentBag(); - - while (true) - { - var workChunks = options.PartitionSize; - var queue = new List(); - for (var i = @from; condition(i, relation, to); i = iterator(i, assignment, step)) - { - var shallowWorkerFactory = workerFactory.Clone(); - queue.Add(Task.Factory.StartNew(j => - { - try - { - shallowWorkerFactory.GenericArguments.Arg1 = (TNumber)j; - shallowWorkerFactory.ExecuteMethod(); - } - catch (Exception e) - { - exceptions.Add(e); - } - }, i, options.CancellationToken, options.CreationOptions, options.Scheduler)); - - workChunks--; - - if (workChunks == 0) - { - @from = Calculator.Calculate(i, assignment, step); - break; - } - } - if (queue.Count == 0) { break; } - await Task.WhenAll(queue).ConfigureAwait(false); - if (workChunks > 1) { break; } - } - if (exceptions.Count > 0) { throw new AggregateException(exceptions); } + return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); } } } \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs index 3e3775d3d..58df9177a 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs @@ -14,9 +14,9 @@ public static partial class ParallelFactory /// The type of the data in the source. /// The sequence to iterate over parallel. /// The delegate that is invoked once per iteration. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -32,9 +32,9 @@ public static Task ForEachAsync(IEnumerable source, ActionThe sequence to iterate over parallel. /// The delegate that is invoked once per iteration. /// The parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T arg, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, T arg, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -52,9 +52,9 @@ public static Task ForEachAsync(IEnumerable source, Action< /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -74,9 +74,9 @@ public static Task ForEachAsync(IEnumerable source, Ac /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -98,9 +98,9 @@ public static Task ForEachAsync(IEnumerable source /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -124,9 +124,9 @@ public static Task ForEachAsync(IEnumerable so /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -134,7 +134,7 @@ public static Task ForEachAsync(IEnumerable(IEnumerable source, ActionFactory workerFactory, Action setup) + private static async Task ForEachCoreAsync(IEnumerable source, ActionFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); diff --git a/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs index a0eec57d4..851297d04 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs @@ -17,10 +17,10 @@ public static partial class ParallelFactory /// The type of the return value of the function delegate . /// The sequence to iterate over parallel. /// The delegate that is invoked once per iteration. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -37,10 +37,10 @@ public static Task> ForEachResultAsyncThe sequence to iterate over parallel. /// The delegate that is invoked once per iteration. /// The parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T arg, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, T arg, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -59,10 +59,10 @@ public static Task> ForEachResultAsyncThe delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -83,10 +83,10 @@ public static Task> ForEachResultAsyncThe first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -109,10 +109,10 @@ public static Task> ForEachResultAsyncThe second parameter of the function delegate . /// The third parameter of the function delegate . /// The fourth parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -137,10 +137,10 @@ public static Task> ForEachResultAsyncThe third parameter of the function delegate . /// The fourth parameter of the function delegate . /// The fifth parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -148,7 +148,7 @@ public static Task> ForEachResultAsync> ForEachResultCoreAsync(IEnumerable source, FuncFactory workerFactory, Action setup) + private static async Task> ForEachResultCoreAsync(IEnumerable source, FuncFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); diff --git a/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs index 2afa3801d..5c0140d92 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; using System.Threading.Tasks; namespace Cuemon.Threading @@ -19,10 +16,10 @@ public static partial class ParallelFactory /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); } /// @@ -37,10 +34,10 @@ public static Task> ForResultAsync(int fro /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T arg, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T arg, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); } /// @@ -57,10 +54,10 @@ public static Task> ForResultAsync(int /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); } /// @@ -79,10 +76,10 @@ public static Task> ForResultAsync /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); } /// @@ -103,10 +100,10 @@ public static Task> ForResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); } /// @@ -128,239 +125,135 @@ public static Task> ForResultAsyncThe fifth parameter of the function delegate . /// The which may be configured. /// A that represents the asynchronous operation. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the parameter of the function delegate . /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T arg, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, T arg, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the fourth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . /// The fourth parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the fourth parameter of the function delegate . /// The type of the fifth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . /// The fourth parameter of the function delegate . /// The fifth parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. - /// A that represents the asynchronous operation. - /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + /// A that represents the asynchronous operation. + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); - } - - private static async Task> ForResultCoreAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, FuncFactory workerFactory, Func condition, Func iterator, Action setup) - where TWorker : Template - where TNumber : struct, IComparable, IEquatable, IConvertible - { - if (condition == null) { condition = Condition; } - if (iterator == null) { iterator = Iterator; } - - var options = Patterns.Configure(setup); - var exceptions = new ConcurrentBag(); - var result = new ConcurrentDictionary(); - - while (true) - { - var workChunks = options.PartitionSize; - var queue = new List(); - for (var i = @from; condition(i, relation, to); i = iterator(i, assignment, step)) - { - var shallowWorkerFactory = workerFactory.Clone(); - queue.Add(Task.Factory.StartNew(j => - { - try - { - var number = (TNumber)j; - shallowWorkerFactory.GenericArguments.Arg1 = number; - var presult = shallowWorkerFactory.ExecuteMethod(); - result.TryAdd(number, presult); - } - catch (Exception e) - { - exceptions.Add(e); - } - }, i, options.CancellationToken, options.CreationOptions, options.Scheduler)); - - workChunks--; - - if (workChunks == 0) - { - @from = Calculator.Calculate(i, assignment, step); - break; - } - } - if (queue.Count == 0) { break; } - await Task.WhenAll(queue).ConfigureAwait(false); - if (workChunks > 1) { break; } - } - if (exceptions.Count > 0) { throw new AggregateException(exceptions); } - return new ReadOnlyCollection(result.Values.ToList()); + return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); } } } \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.cs b/src/Cuemon.Threading/ParallelFactory.cs new file mode 100644 index 000000000..e4ab515eb --- /dev/null +++ b/src/Cuemon.Threading/ParallelFactory.cs @@ -0,0 +1,9 @@ +namespace Cuemon.Threading +{ + /// + /// Provides a factory based way to encapsulate and re-use existing code while adding support for typically long-running parallel loops and regions. + /// + public static partial class ParallelFactory + { + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt index 04306ea5c..49edaaa99 100644 --- a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt @@ -1,6 +1,10 @@ Version: 6.0.0 Availability: NET Standard 2.0   +# Breaking Changes +- REMOVED ThreadPoolUtility class from the Cuemon.Threading namespace +- CHANGED ParallelFactory class in the Cuemon.Threading namespace; all members refactored to Async signature +  # New Features - ADDED TimerFactory class in the Cuemon.Threading namespace that provides access to factory methods for creating and configuring Timer instances - ADDED AsyncOptions class in the Cuemon.Threading namespace that specifies options that is related to asynchronous operations diff --git a/test/Cuemon.Threading.Tests/ForAsyncTest.cs b/test/Cuemon.Threading.Tests/ForAsyncTest.cs deleted file mode 100644 index c1efe28ff..000000000 --- a/test/Cuemon.Threading.Tests/ForAsyncTest.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Concurrent; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.Extensions.Xunit; -using Xunit; -using Xunit.Abstractions; - -namespace Cuemon.Threading -{ - public class ForAsyncTest : Test - { - public ForAsyncTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task ForAsync_ShouldRunOn1000Threads() - { - var cb = new ConcurrentBag(); - await ParallelFactory.ForAsync(0, 1000, i => - { - Thread.Sleep(50); // todo: refactor to true async method - cb.Add(Thread.CurrentThread.ManagedThreadId); - }, o => o.PartitionSize = 64); - - Assert.Equal(1000, cb.Count); - } - } -} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs b/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs index 994b72bf0..040bfb80b 100644 --- a/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs @@ -1,5 +1,4 @@ using System.Collections.Concurrent; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Cuemon.Extensions.Xunit; diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs new file mode 100644 index 000000000..cd0829408 --- /dev/null +++ b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Threading +{ + public class ParallelFactoryAsyncTest : Test + { + public ParallelFactoryAsyncTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ForAsync_ShouldRunConcurrent() + { + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + await ParallelFactory.ForAsync(0, count, async (i, ct) => + { + await Task.Delay(50, ct); + cb.Add(i); + }, o => o.PartitionSize = 64); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForAsync_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + + await Assert.ThrowsAsync(async () => + { + await ParallelFactory.ForAsync(0, count, async (i, ct) => + { + if (i > 500) { cts.Cancel(); } + await Task.Delay(Generate.RandomNumber(25, 75), ct); + cb.Add(i); + }, o => o.CancellationToken = cts.Token); + }); + + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForAsync_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + await ParallelFactory.ForAsync(0, count, async (i, ct) => + { + await Task.Delay(1000, ct); + cb.Add(i); + }); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + await ParallelFactory.ForAsync(0, count, async (i, ct) => + { + await Task.Delay(100, ct); + cb.Add(i); + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs new file mode 100644 index 000000000..b1ec9dae2 --- /dev/null +++ b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Threading +{ + public class ParallelFactoryTest : Test + { + public ParallelFactoryTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void For_ShouldRunConcurrent() + { + var atMostExpectedCount = 500; + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + ParallelFactory.For(0, count, i => + { + Thread.Sleep(50); + cb.Add(i); + }, o => o.CreationOptions = TaskCreationOptions.None); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i))); + } + + [Fact] + public void For_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + var x = 0; + Assert.Throws(() => + { + ParallelFactory.For(0, count, i => + { + Interlocked.Increment(ref x); + if (i > 500) { cts.Cancel(); } + Thread.Sleep(Generate.RandomNumber(25, 75)); + cb.Add(i); + }, o => + { + o.CancellationToken = cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); + }); + + Thread.Sleep(500); // wait for possible background threads being canceled + + TestOutput.WriteLine(x.ToString()); + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 300, 600); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void For_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + ParallelFactory.For(0, count, i => + { + Thread.Sleep(1000); + cb.Add(i); + }); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i))); + } + + [Fact] + public void For_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + ParallelFactory.For(0, count, i => + { + Thread.Sleep(100); + cb.Add(i); + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + + } +} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/WhileAsyncTest.cs b/test/Cuemon.Threading.Tests/WhileAsyncTest.cs index 3c6eb4f26..3f8722571 100644 --- a/test/Cuemon.Threading.Tests/WhileAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/WhileAsyncTest.cs @@ -1,6 +1,4 @@ using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Cuemon.Extensions.Xunit; @@ -20,7 +18,7 @@ public async Task WhileAsyncTest_ShouldRunOn1000Threads() { var cb = new ConcurrentBag(); var fakeReader = new ConcurrentQueue(Generate.RangeOf(1000, i => i)); - await ParallelFactory.WhileAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => cq.TryDequeue(out var x), i => + await AdvancedParallelFactory.WhileAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => cq.TryDequeue(out var x), i => { Thread.Sleep(50); // todo: refactor to true async method cb.Add(Thread.CurrentThread.ManagedThreadId); diff --git a/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs b/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs index 9dd383c5b..d27c4ba97 100644 --- a/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs @@ -19,7 +19,7 @@ public async Task WhileResultAsyncTest_ShouldRunOn1000Threads() { var cb = new ConcurrentBag(); var fakeReader = new ConcurrentQueue(Generate.RangeOf(1000, i => i)); - var result = await ParallelFactory.WhileResultAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => + var result = await AdvancedParallelFactory.WhileResultAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => { if (cq.TryDequeue(out var x)) { From ee0df4de5c400f577b774451d17dc61b11609649 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 9 Sep 2020 03:04:41 +0200 Subject: [PATCH 161/385] Revert "Phrasing." This reverts commit fdd3eff5ec101988c32f18ba499063c78e34acff. --- .../TransientOperation.Async.cs | 12 +- .../AdvancedParallelFactory.For.cs | 230 ---------------- .../AdvancedParallelFactory.ForAsync.cs | 203 -------------- .../AdvancedParallelFactory.ForResultAsync.cs | 237 ---------------- .../AdvancedParallelFactory.cs | 54 ---- src/Cuemon.Threading/AsyncWorkloadOptions.cs | 37 --- src/Cuemon.Threading/ParallelFactory.For.cs | 236 ---------------- .../ParallelFactory.ForAsync.cs | 254 ++++++++++++++---- .../ParallelFactory.ForEachAsync.cs | 26 +- .../ParallelFactory.ForEachResultAsync.cs | 26 +- .../ParallelFactory.ForResultAsync.cs | 193 ++++++++++--- ...Async.cs => ParallelFactory.WhileAsync.cs} | 31 ++- ...cs => ParallelFactory.WhileResultAsync.cs} | 16 +- src/Cuemon.Threading/ParallelFactory.cs | 9 - .../Properties/PackageReleaseNotes.txt | 4 - ...actoryOptions.cs => TaskFactoryOptions.cs} | 19 +- test/Cuemon.Threading.Tests/ForAsyncTest.cs | 29 ++ .../ForEachAsyncTest.cs | 1 + .../ParallelFactoryAsyncTest.cs | 94 ------- .../ParallelFactoryTest.cs | 104 ------- test/Cuemon.Threading.Tests/WhileAsyncTest.cs | 4 +- .../WhileResultAsyncTest.cs | 2 +- 22 files changed, 453 insertions(+), 1368 deletions(-) delete mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.For.cs delete mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs delete mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs delete mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.cs delete mode 100644 src/Cuemon.Threading/AsyncWorkloadOptions.cs delete mode 100644 src/Cuemon.Threading/ParallelFactory.For.cs rename src/Cuemon.Threading/{AdvancedParallelFactory.WhileAsync.cs => ParallelFactory.WhileAsync.cs} (91%) rename src/Cuemon.Threading/{AdvancedParallelFactory.WhileResultAsync.cs => ParallelFactory.WhileResultAsync.cs} (97%) delete mode 100644 src/Cuemon.Threading/ParallelFactory.cs rename src/Cuemon.Threading/{AsyncTaskFactoryOptions.cs => TaskFactoryOptions.cs} (72%) create mode 100644 test/Cuemon.Threading.Tests/ForAsyncTest.cs delete mode 100644 test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs delete mode 100644 test/Cuemon.Threading.Tests/ParallelFactoryTest.cs diff --git a/src/Cuemon.Resilience/TransientOperation.Async.cs b/src/Cuemon.Resilience/TransientOperation.Async.cs index eb5856ac6..f1349566d 100644 --- a/src/Cuemon.Resilience/TransientOperation.Async.cs +++ b/src/Cuemon.Resilience/TransientOperation.Async.cs @@ -10,7 +10,7 @@ public static partial class TransientOperation /// Repetitively executes the specified until the operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// /// The type of the return value of the function delegate . - /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The token to monitor for cancellation requests. The default value is . /// The which may be configured. /// The result from the . @@ -40,7 +40,7 @@ public static Task WithFuncAsync(Func /// The type of the parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The parameter of the function delegate . /// The token to monitor for cancellation requests. The default value is . /// The which may be configured. @@ -72,7 +72,7 @@ public static Task WithFuncAsync(FuncThe type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The token to monitor for cancellation requests. The default value is . @@ -106,7 +106,7 @@ public static Task WithFuncAsync(FuncThe type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . @@ -142,7 +142,7 @@ public static Task WithFuncAsync(FuncThe type of the third parameter of the function delegate . /// The type of the fourth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . @@ -180,7 +180,7 @@ public static Task WithFuncAsync(FuncThe type of the fourth parameter of the function delegate . /// The type of the fifth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.For.cs b/src/Cuemon.Threading/AdvancedParallelFactory.For.cs deleted file mode 100644 index 5a720cb39..000000000 --- a/src/Cuemon.Threading/AdvancedParallelFactory.For.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Cuemon.Threading -{ - public static partial class AdvancedParallelFactory - { - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The type of the parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T arg, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from, arg), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from, arg1, arg2), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from, arg1, arg2, arg3), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The fourth parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from, arg1, arg2, arg3, arg4), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The fourth parameter of the delegate . - /// The fifth parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - public static void For(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - ForCore(from, relation, to, assignment, step, ActionFactory.Create(worker, from, arg1, arg2, arg3, arg4, arg5), condition, iterator, setup); - } - - private static void ForCore(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, ActionFactory workerFactory, Func condition, Func iterator, Action setup) - where TWorker : Template - where TNumber : struct, IComparable, IEquatable, IConvertible - { - if (condition == null) { condition = Condition; } - if (iterator == null) { iterator = Iterator; } - - var options = Patterns.Configure(setup); - var exceptions = new ConcurrentBag(); - - TNumber processed = default; - while (true) - { - var workChunks = options.PartitionSize; - var queue = new List(); - for (var i = from; condition(i, relation, to); i = iterator(i, assignment, step)) - { - if (options.CancellationToken.IsCancellationRequested) - { - queue.Clear(); - break; - } - queue.Add(Task.Factory.StartNew(j => - { - var shallowWorkerFactory = workerFactory.Clone(); - try - { - shallowWorkerFactory.GenericArguments.Arg1 = (TNumber)j; - shallowWorkerFactory.ExecuteMethod(); - } - catch (Exception e) - { - exceptions.Add(e); - } - }, i, options.CancellationToken, options.CreationOptions, options.Scheduler)); - - processed = i; - workChunks--; - - if (workChunks == 0) { break; } - } - from = Calculator.Calculate(processed, assignment, step); - if (queue.Count == 0) { break; } - - if (options.CancellationToken.IsCancellationRequested) - { - queue.Clear(); - break; - } - - try - { - Task.WaitAll(queue.ToArray(), options.CancellationToken); - } - catch (OperationCanceledException oce) - { - exceptions.Add(oce); - } - - if (workChunks > 1) { break; } - } - if (exceptions.Count > 0) { throw new AggregateException(exceptions); } - } - } -} \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs deleted file mode 100644 index 0425c93e3..000000000 --- a/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace Cuemon.Threading -{ - public static partial class AdvancedParallelFactory - { - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The based function delegate that is invoked once per iteration. - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The type of the parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The based function delegate that is invoked once per iteration. - /// The parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T arg, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from, arg), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The based function delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from, arg1, arg2), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The based function delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from, arg1, arg2, arg3), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The based function delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The fourth parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from, arg1, arg2, arg3, arg4), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The based function delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The fourth parameter of the delegate . - /// The fifth parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - return ForCoreAsync(from, relation, to, assignment, step, TaskActionFactory.Create(worker, from, arg1, arg2, arg3, arg4, arg5), condition, iterator, setup); - } - - private static async Task ForCoreAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, TaskActionFactory workerFactory, Func condition, Func iterator, Action setup) - where TWorker : Template - where TNumber : struct, IComparable, IEquatable, IConvertible - { - if (condition == null) { condition = Condition; } - if (iterator == null) { iterator = Iterator; } - - var options = Patterns.Configure(setup); - - TNumber processed = default; - while (true) - { - var workChunks = options.PartitionSize; - var queue = new List(); - for (var i = from; condition(i, relation, to); i = iterator(i, assignment, step)) - { - workerFactory.GenericArguments.Arg1 = i; - queue.Add(workerFactory.ExecuteMethodAsync(options.CancellationToken)); - - processed = i; - workChunks--; - - if (workChunks == 0) { break; } - } - from = Calculator.Calculate(processed, assignment, step); - if (queue.Count == 0) { break; } - await Task.WhenAll(queue).ConfigureAwait(false); - if (workChunks > 1) { break; } - } - } - } -} \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs deleted file mode 100644 index 5f0dd677a..000000000 --- a/src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; - -namespace Cuemon.Threading -{ - public static partial class AdvancedParallelFactory - { - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. - /// - /// The type of the number used with the loop control variable. - /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultCoreAsync(from, relation, to, assignment, step, FuncFactory.Create(worker, from), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. - /// - /// The type of the number used with the loop control variable. - /// The type of the parameter of the function delegate . - /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T arg, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, from, arg); - return ForResultCoreAsync(from, relation, to, assignment, step, wf, condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultCoreAsync(from, relation, to, assignment, step, FuncFactory.Create(worker, from, arg1, arg2), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultCoreAsync(from, relation, to, assignment, step, FuncFactory.Create(worker, from, arg1, arg2, arg3), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultCoreAsync(from, relation, to, assignment, step, FuncFactory.Create(worker, from, arg1, arg2, arg3, arg4), condition, iterator, setup); - } - - /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. - /// - /// The type of the number used with the loop control variable. - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. - /// The delegate that is invoked once per iteration. - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. - /// A that represents the asynchronous operation. - /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultCoreAsync(from, relation, to, assignment, step, FuncFactory.Create(worker, from, arg1, arg2, arg3, arg4, arg5), condition, iterator, setup); - } - - private static async Task> ForResultCoreAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, FuncFactory workerFactory, Func condition, Func iterator, Action setup) - where TWorker : Template - where TNumber : struct, IComparable, IEquatable, IConvertible - { - if (condition == null) { condition = Condition; } - if (iterator == null) { iterator = Iterator; } - - var options = Patterns.Configure(setup); - var exceptions = new ConcurrentBag(); - var result = new ConcurrentDictionary(); - - while (true) - { - var workChunks = options.PartitionSize; - var queue = new List(); - for (var i = from; condition(i, relation, to); i = iterator(i, assignment, step)) - { - var shallowWorkerFactory = workerFactory.Clone(); - queue.Add(Task.Factory.StartNew(j => - { - try - { - var number = (TNumber)j; - shallowWorkerFactory.GenericArguments.Arg1 = number; - var presult = shallowWorkerFactory.ExecuteMethod(); - result.TryAdd(number, presult); - } - catch (Exception e) - { - exceptions.Add(e); - } - }, i, options.CancellationToken, options.CreationOptions, options.Scheduler)); - - workChunks--; - - if (workChunks == 0) - { - from = Calculator.Calculate(i, assignment, step); - break; - } - } - if (queue.Count == 0) { break; } - await Task.WhenAll(queue).ConfigureAwait(false); - if (workChunks > 1) { break; } - } - if (exceptions.Count > 0) { throw new AggregateException(exceptions); } - return new ReadOnlyCollection(result.Values.ToList()); - } - } -} \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.cs b/src/Cuemon.Threading/AdvancedParallelFactory.cs deleted file mode 100644 index fb04f9e11..000000000 --- a/src/Cuemon.Threading/AdvancedParallelFactory.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; - -namespace Cuemon.Threading -{ - /// - /// Provides a factory based way to work with advanced scenarios that encapsulate and re-use existing code while adding support for typically long-running parallel loops and regions. - /// - public static partial class AdvancedParallelFactory - { - /// - /// Provides a default implementation of a for-iterator callback method. - /// - /// The type of the counter in a for-loop. - /// The current value of the counter in a for-loop. - /// One of the enumeration values that specifies the rules to apply as the assignment operator for left-hand operand and right-hand operand . - /// The value to assign to according to the rule specified by . - /// The computed result of having the of . - public static T Iterator(T current, AssignmentOperator assignment, T step) where T : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - return Calculator.Calculate(current, assignment, step); - } - - /// - /// Provides a default implementation of a for-condition callback method. - /// - /// The type of the counter in a for-loop. - /// The current value of the counter in a for-loop. - /// One of the enumeration values that specifies the rules to apply as the relational operator for left-hand operand and right-hand operand . - /// The amount of repeats to do according to the rules specified by . - /// true if does not meet the condition of and ; otherwise false. - public static bool Condition(T current, RelationalOperator relational, T repeats) where T : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - switch (relational) - { - case RelationalOperator.Equal: - return current.Equals(repeats); - case RelationalOperator.GreaterThan: - return current.CompareTo(repeats) > 0; - case RelationalOperator.GreaterThanOrEqual: - return current.CompareTo(repeats) >= 0; - case RelationalOperator.LessThan: - return current.CompareTo(repeats) < 0; - case RelationalOperator.LessThanOrEqual: - return current.CompareTo(repeats) <= 0; - case RelationalOperator.NotEqual: - return !current.Equals(repeats); - default: - throw new ArgumentOutOfRangeException(nameof(relational)); - } - } - } -} \ No newline at end of file diff --git a/src/Cuemon.Threading/AsyncWorkloadOptions.cs b/src/Cuemon.Threading/AsyncWorkloadOptions.cs deleted file mode 100644 index 43781d81a..000000000 --- a/src/Cuemon.Threading/AsyncWorkloadOptions.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - -namespace Cuemon.Threading -{ - /// - /// Configuration options for . - /// - public class AsyncWorkloadOptions : AsyncOptions - { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// 2 x - /// - /// - /// - public AsyncWorkloadOptions() - { - PartitionSize = 2 * Environment.ProcessorCount; - } - - /// - /// Gets or sets the size of the partition to allocate work to a set of tasks. - /// - /// The size of the partition to allocate work to a set of tasks. - public int PartitionSize { get; set; } - } -} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.For.cs b/src/Cuemon.Threading/ParallelFactory.For.cs deleted file mode 100644 index ababd29c6..000000000 --- a/src/Cuemon.Threading/ParallelFactory.For.cs +++ /dev/null @@ -1,236 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Cuemon.Threading -{ - public static partial class ParallelFactory - { - /// - /// Executes a parallel for loop. - /// - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(int fromInclusive, int toExclusive, Action worker, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); - } - - /// - /// Executes a parallel for loop. - /// - /// The type of the parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The parameter of the delegate . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(int fromInclusive, int toExclusive, Action worker, T arg, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); - } - - /// - /// Executes a parallel for loop. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); - } - - /// - /// Executes a parallel for loop. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); - } - - /// - /// Executes a parallel for loop. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The fourth parameter of the delegate . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); - } - - /// - /// Executes a parallel for loop. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The fourth parameter of the delegate . - /// The fifth parameter of the delegate . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); - } - - /// - /// Executes a parallel for loop. - /// - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(long fromInclusive, long toExclusive, Action worker, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); - } - - /// - /// Executes a parallel for loop. - /// - /// The type of the parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The parameter of the delegate . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(long fromInclusive, long toExclusive, Action worker, T arg, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); - } - - /// - /// Executes a parallel for loop. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); - } - - /// - /// Executes a parallel for loop. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); - } - - /// - /// Executes a parallel for loop. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The fourth parameter of the delegate . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); - } - - /// - /// Executes a parallel for loop. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. - /// The delegate that is invoked once per iteration. - /// The first parameter of the delegate . - /// The second parameter of the delegate . - /// The third parameter of the delegate . - /// The fourth parameter of the delegate . - /// The fifth parameter of the delegate . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) - { - Validator.ThrowIfNull(worker, nameof(worker)); - AdvancedParallelFactory.For(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); - } - } -} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.ForAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForAsync.cs index 1b404de9d..aef84e397 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForAsync.cs @@ -1,23 +1,68 @@ using System; -using System.Threading; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Threading.Tasks; namespace Cuemon.Threading { public static partial class ParallelFactory { + /// + /// Provides a default implementation of a for-iterator callback method. + /// + /// The type of the counter in a for-loop. + /// The current value of the counter in a for-loop. + /// One of the enumeration values that specifies the rules to apply as the assignment operator for left-hand operand and right-hand operand . + /// The value to assign to according to the rule specified by . + /// The computed result of having the of . + public static T Iterator(T current, AssignmentOperator assignment, T step) where T : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + return Calculator.Calculate(current, assignment, step); + } + + /// + /// Provides a default implementation of a for-condition callback method. + /// + /// The type of the counter in a for-loop. + /// The current value of the counter in a for-loop. + /// One of the enumeration values that specifies the rules to apply as the relational operator for left-hand operand and right-hand operand . + /// The amount of repeats to do according to the rules specified by . + /// true if does not meet the condition of and ; otherwise false. + public static bool Condition(T current, RelationalOperator relational, T repeats) where T : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + switch (relational) + { + case RelationalOperator.Equal: + return current.Equals(repeats); + case RelationalOperator.GreaterThan: + return current.CompareTo(repeats) > 0; + case RelationalOperator.GreaterThanOrEqual: + return current.CompareTo(repeats) >= 0; + case RelationalOperator.LessThan: + return current.CompareTo(repeats) < 0; + case RelationalOperator.LessThanOrEqual: + return current.CompareTo(repeats) <= 0; + case RelationalOperator.NotEqual: + return !current.Equals(repeats); + default: + throw new ArgumentOutOfRangeException(nameof(relational)); + } + } + /// /// Executes a parallel for loop. /// /// The start index, inclusive. /// The end index, exclusive. /// The delegate that is invoked once per iteration. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); + return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); } /// @@ -28,12 +73,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, FuncThe end index, exclusive. /// The delegate that is invoked once per iteration. /// The parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T arg, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T arg, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); + return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); } /// @@ -46,12 +91,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, FuncThe delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); + return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); } /// @@ -66,12 +111,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, FuncThe first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); + return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); } /// @@ -88,12 +133,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, Func /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); + return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); } /// @@ -112,126 +157,225 @@ public static Task ForAsync(int fromInclusive, int toExclusive, /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); } /// - /// Executes a parallel for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. /// - /// The start index, inclusive. - /// The end index, exclusive. + /// The type of the number used with the loop control variable. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. - /// The which may be configured. + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, Action setup = null) + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); + var wf = ActionFactory.Create(worker, default); + return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); } /// - /// Executes a parallel for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. /// + /// The type of the number used with the loop control variable. /// The type of the parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. /// The parameter of the delegate . - /// The which may be configured. + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T arg, Action setup = null) + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T arg, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); + var wf = ActionFactory.Create(worker, default, arg); + return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); } /// - /// Executes a parallel for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. /// + /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The which may be configured. + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); + var wf = ActionFactory.Create(worker, default, arg1, arg2); + return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); } /// - /// Executes a parallel for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. /// + /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . /// The type of the third parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The which may be configured. + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); + var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3); + return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); } /// - /// Executes a parallel for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. /// + /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . /// The type of the third parameter of the delegate . /// The type of the fourth parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The which may be configured. + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); + var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4); + return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); } /// - /// Executes a parallel for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. /// + /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . /// The type of the third parameter of the delegate . /// The type of the fourth parameter of the delegate . /// The type of the fifth parameter of the delegate . - /// The start index, inclusive. - /// The end index, exclusive. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The which may be configured. + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5); + return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + } + + private static async Task ForCoreAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, ActionFactory workerFactory, Func condition, Func iterator, Action setup) + where TWorker : Template + where TNumber : struct, IComparable, IEquatable, IConvertible + { + if (condition == null) { condition = Condition; } + if (iterator == null) { iterator = Iterator; } + + var options = Patterns.Configure(setup); + var exceptions = new ConcurrentBag(); + + while (true) + { + var workChunks = options.PartitionSize; + var queue = new List(); + for (var i = @from; condition(i, relation, to); i = iterator(i, assignment, step)) + { + var shallowWorkerFactory = workerFactory.Clone(); + queue.Add(Task.Factory.StartNew(j => + { + try + { + shallowWorkerFactory.GenericArguments.Arg1 = (TNumber)j; + shallowWorkerFactory.ExecuteMethod(); + } + catch (Exception e) + { + exceptions.Add(e); + } + }, i, options.CancellationToken, options.CreationOptions, options.Scheduler)); + + workChunks--; + + if (workChunks == 0) + { + @from = Calculator.Calculate(i, assignment, step); + break; + } + } + if (queue.Count == 0) { break; } + await Task.WhenAll(queue).ConfigureAwait(false); + if (workChunks > 1) { break; } + } + if (exceptions.Count > 0) { throw new AggregateException(exceptions); } } } } \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs index 58df9177a..3e3775d3d 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs @@ -14,9 +14,9 @@ public static partial class ParallelFactory /// The type of the data in the source. /// The sequence to iterate over parallel. /// The delegate that is invoked once per iteration. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -32,9 +32,9 @@ public static Task ForEachAsync(IEnumerable source, ActionThe sequence to iterate over parallel. /// The delegate that is invoked once per iteration. /// The parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T arg, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, T arg, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -52,9 +52,9 @@ public static Task ForEachAsync(IEnumerable source, Action< /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -74,9 +74,9 @@ public static Task ForEachAsync(IEnumerable source, Ac /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -98,9 +98,9 @@ public static Task ForEachAsync(IEnumerable source /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -124,9 +124,9 @@ public static Task ForEachAsync(IEnumerable so /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -134,7 +134,7 @@ public static Task ForEachAsync(IEnumerable(IEnumerable source, ActionFactory workerFactory, Action setup) + private static async Task ForEachCoreAsync(IEnumerable source, ActionFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); diff --git a/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs index 851297d04..a0eec57d4 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs @@ -17,10 +17,10 @@ public static partial class ParallelFactory /// The type of the return value of the function delegate . /// The sequence to iterate over parallel. /// The delegate that is invoked once per iteration. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -37,10 +37,10 @@ public static Task> ForEachResultAsyncThe sequence to iterate over parallel. /// The delegate that is invoked once per iteration. /// The parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T arg, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, T arg, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -59,10 +59,10 @@ public static Task> ForEachResultAsyncThe delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -83,10 +83,10 @@ public static Task> ForEachResultAsyncThe first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -109,10 +109,10 @@ public static Task> ForEachResultAsyncThe second parameter of the function delegate . /// The third parameter of the function delegate . /// The fourth parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -137,10 +137,10 @@ public static Task> ForEachResultAsyncThe third parameter of the function delegate . /// The fourth parameter of the function delegate . /// The fifth parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); @@ -148,7 +148,7 @@ public static Task> ForEachResultAsync> ForEachResultCoreAsync(IEnumerable source, FuncFactory workerFactory, Action setup) + private static async Task> ForEachResultCoreAsync(IEnumerable source, FuncFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); diff --git a/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs index 5c0140d92..2afa3801d 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; using System.Threading.Tasks; namespace Cuemon.Threading @@ -16,10 +19,10 @@ public static partial class ParallelFactory /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); + return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); } /// @@ -34,10 +37,10 @@ public static Task> ForResultAsync(int fro /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T arg, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T arg, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); + return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); } /// @@ -54,10 +57,10 @@ public static Task> ForResultAsync(int /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); + return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); } /// @@ -76,10 +79,10 @@ public static Task> ForResultAsync /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); + return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); } /// @@ -100,10 +103,10 @@ public static Task> ForResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); + return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); } /// @@ -125,135 +128,239 @@ public static Task> ForResultAsyncThe fifth parameter of the function delegate . /// The which may be configured. /// A that represents the asynchronous operation. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); } /// - /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. /// + /// The type of the number used with the loop control variable. /// The type of the return value of the function delegate . - /// The start index, inclusive. - /// The end index, exclusive. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, Action setup = null) + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); + var wf = FuncFactory.Create(worker, default); + return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); } /// - /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. /// + /// The type of the number used with the loop control variable. /// The type of the parameter of the function delegate . /// The type of the return value of the function delegate . - /// The start index, inclusive. - /// The end index, exclusive. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. /// The parameter of the function delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, T arg, Action setup = null) + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T arg, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); + var wf = FuncFactory.Create(worker, default, arg); + return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); } /// - /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. /// + /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the return value of the function delegate . - /// The start index, inclusive. - /// The end index, exclusive. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); + var wf = FuncFactory.Create(worker, default, arg1, arg2); + return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); } /// - /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. /// + /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the return value of the function delegate . - /// The start index, inclusive. - /// The end index, exclusive. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); + var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3); + return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); } /// - /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. /// + /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the fourth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The start index, inclusive. - /// The end index, exclusive. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . /// The fourth parameter of the function delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); + var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4); + return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); } /// - /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. /// + /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the fourth parameter of the function delegate . /// The type of the fifth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The start index, inclusive. - /// The end index, exclusive. + /// The initial value of the loop control variable. + /// The relation between the loop control variable and . + /// The conditional value of the loop control variable. + /// The assignment statement of the loop control variable using . + /// The value to assign the loop control variable. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . /// The fourth parameter of the function delegate . /// The fifth parameter of the function delegate . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . /// The which may be configured. - /// A that represents the asynchronous operation. - public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) + where TNumber : struct, IComparable, IEquatable, IConvertible { + Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - return AdvancedParallelFactory.ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5); + return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + } + + private static async Task> ForResultCoreAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, FuncFactory workerFactory, Func condition, Func iterator, Action setup) + where TWorker : Template + where TNumber : struct, IComparable, IEquatable, IConvertible + { + if (condition == null) { condition = Condition; } + if (iterator == null) { iterator = Iterator; } + + var options = Patterns.Configure(setup); + var exceptions = new ConcurrentBag(); + var result = new ConcurrentDictionary(); + + while (true) + { + var workChunks = options.PartitionSize; + var queue = new List(); + for (var i = @from; condition(i, relation, to); i = iterator(i, assignment, step)) + { + var shallowWorkerFactory = workerFactory.Clone(); + queue.Add(Task.Factory.StartNew(j => + { + try + { + var number = (TNumber)j; + shallowWorkerFactory.GenericArguments.Arg1 = number; + var presult = shallowWorkerFactory.ExecuteMethod(); + result.TryAdd(number, presult); + } + catch (Exception e) + { + exceptions.Add(e); + } + }, i, options.CancellationToken, options.CreationOptions, options.Scheduler)); + + workChunks--; + + if (workChunks == 0) + { + @from = Calculator.Calculate(i, assignment, step); + break; + } + } + if (queue.Count == 0) { break; } + await Task.WhenAll(queue).ConfigureAwait(false); + if (workChunks > 1) { break; } + } + if (exceptions.Count > 0) { throw new AggregateException(exceptions); } + return new ReadOnlyCollection(result.Values.ToList()); } } } \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs b/src/Cuemon.Threading/ParallelFactory.WhileAsync.cs similarity index 91% rename from src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs rename to src/Cuemon.Threading/ParallelFactory.WhileAsync.cs index 6c137baec..032cd47cb 100644 --- a/src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.WhileAsync.cs @@ -6,7 +6,10 @@ namespace Cuemon.Threading { - public static partial class AdvancedParallelFactory + /// + /// Provides a factory based way to encapsulate and re-use existing code while adding support for typically long-running parallel loops and regions. + /// + public static partial class ParallelFactory { /// /// Executes a parallel while loop. @@ -17,9 +20,9 @@ public static partial class AdvancedParallelFactory /// The function delegate that is responsible for the while loop condition. /// The function delegate that provides data from the specified . /// The delegate that will perform work while evaluates true. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -39,9 +42,9 @@ public static Task WhileAsync(TReader reader, Func /// The function delegate that provides data from the specified . /// The delegate that will perform work while evaluates true. /// The parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T arg, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T arg, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -63,9 +66,9 @@ public static Task WhileAsync(TReader reader, FuncThe delegate that will perform work while evaluates true. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -89,9 +92,9 @@ public static Task WhileAsync(TReader reader, FuncThe first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -117,9 +120,9 @@ public static Task WhileAsync(TReader reader, Fun /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -147,9 +150,9 @@ public static Task WhileAsync(TReader reader, /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -158,7 +161,7 @@ public static Task WhileAsync(TReader rea return WhileCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); } - private static async Task WhileCoreAsync(ForwardIterator iterator, ActionFactory workerFactory, Action setup) + private static async Task WhileCoreAsync(ForwardIterator iterator, ActionFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs b/src/Cuemon.Threading/ParallelFactory.WhileResultAsync.cs similarity index 97% rename from src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs rename to src/Cuemon.Threading/ParallelFactory.WhileResultAsync.cs index 283caf170..0fdc25ea0 100644 --- a/src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.WhileResultAsync.cs @@ -8,7 +8,7 @@ namespace Cuemon.Threading { - public static partial class AdvancedParallelFactory + public static partial class ParallelFactory { /// /// Executes a parallel while loop where the return value of the function delegate is stored in the same order as the while loop evaluates true. @@ -23,7 +23,7 @@ public static partial class AdvancedParallelFactory /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -47,7 +47,7 @@ public static Task> WhileResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T arg, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T arg, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -73,7 +73,7 @@ public static Task> WhileResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -101,7 +101,7 @@ public static Task> WhileResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -131,7 +131,7 @@ public static Task> WhileResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -163,7 +163,7 @@ public static Task> WhileResultAsyncThe which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); @@ -172,7 +172,7 @@ public static Task> WhileResultAsync(reader, condition, provider), wf, setup); } - private static async Task> WhileResultCoreAsync(ForwardIterator iterator, FuncFactory workerFactory, Action setup) + private static async Task> WhileResultCoreAsync(ForwardIterator iterator, FuncFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); diff --git a/src/Cuemon.Threading/ParallelFactory.cs b/src/Cuemon.Threading/ParallelFactory.cs deleted file mode 100644 index e4ab515eb..000000000 --- a/src/Cuemon.Threading/ParallelFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Cuemon.Threading -{ - /// - /// Provides a factory based way to encapsulate and re-use existing code while adding support for typically long-running parallel loops and regions. - /// - public static partial class ParallelFactory - { - } -} \ No newline at end of file diff --git a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt index 49edaaa99..04306ea5c 100644 --- a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt @@ -1,10 +1,6 @@ Version: 6.0.0 Availability: NET Standard 2.0   -# Breaking Changes -- REMOVED ThreadPoolUtility class from the Cuemon.Threading namespace -- CHANGED ParallelFactory class in the Cuemon.Threading namespace; all members refactored to Async signature -  # New Features - ADDED TimerFactory class in the Cuemon.Threading namespace that provides access to factory methods for creating and configuring Timer instances - ADDED AsyncOptions class in the Cuemon.Threading namespace that specifies options that is related to asynchronous operations diff --git a/src/Cuemon.Threading/AsyncTaskFactoryOptions.cs b/src/Cuemon.Threading/TaskFactoryOptions.cs similarity index 72% rename from src/Cuemon.Threading/AsyncTaskFactoryOptions.cs rename to src/Cuemon.Threading/TaskFactoryOptions.cs index 5c42402f8..cf0cbc0cf 100644 --- a/src/Cuemon.Threading/AsyncTaskFactoryOptions.cs +++ b/src/Cuemon.Threading/TaskFactoryOptions.cs @@ -4,22 +4,22 @@ namespace Cuemon.Threading { /// - /// Configuration options for . + /// Configuration options for . /// - public class AsyncTaskFactoryOptions : AsyncWorkloadOptions + public class TaskFactoryOptions : AsyncOptions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// - /// The following table shows the initial property values for an instance of . + /// The following table shows the initial property values for an instance of . /// /// /// Property /// Initial Value /// /// - /// + /// /// 2 x /// /// @@ -32,12 +32,19 @@ public class AsyncTaskFactoryOptions : AsyncWorkloadOptions /// /// /// - public AsyncTaskFactoryOptions() + public TaskFactoryOptions() { CreationOptions = TaskCreationOptions.LongRunning; Scheduler = TaskScheduler.Current; + PartitionSize = 2 * Environment.ProcessorCount; } + /// + /// Gets or sets the size of the partition to allocate work to a set of tasks. + /// + /// The size of the partition to allocate work to a set of tasks. + public int PartitionSize { get; set; } + /// /// Gets or sets the used to create the task. /// diff --git a/test/Cuemon.Threading.Tests/ForAsyncTest.cs b/test/Cuemon.Threading.Tests/ForAsyncTest.cs new file mode 100644 index 000000000..c1efe28ff --- /dev/null +++ b/test/Cuemon.Threading.Tests/ForAsyncTest.cs @@ -0,0 +1,29 @@ +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Threading +{ + public class ForAsyncTest : Test + { + public ForAsyncTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ForAsync_ShouldRunOn1000Threads() + { + var cb = new ConcurrentBag(); + await ParallelFactory.ForAsync(0, 1000, i => + { + Thread.Sleep(50); // todo: refactor to true async method + cb.Add(Thread.CurrentThread.ManagedThreadId); + }, o => o.PartitionSize = 64); + + Assert.Equal(1000, cb.Count); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs b/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs index 040bfb80b..994b72bf0 100644 --- a/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Cuemon.Extensions.Xunit; diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs deleted file mode 100644 index cd0829408..000000000 --- a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.Extensions.Xunit; -using Xunit; -using Xunit.Abstractions; - -namespace Cuemon.Threading -{ - public class ParallelFactoryAsyncTest : Test - { - public ParallelFactoryAsyncTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task ForAsync_ShouldRunConcurrent() - { - var count = 1000; - var expected = Generate.RangeOf(count, i => i); - var cb = new ConcurrentBag(); - - await ParallelFactory.ForAsync(0, count, async (i, ct) => - { - await Task.Delay(50, ct); - cb.Add(i); - }, o => o.PartitionSize = 64); - - Assert.Equal(count, cb.Count); - Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); - } - - [Fact] - public async Task ForAsync_ShouldRunConcurrent_IgniteCancellation() - { - var count = 1000; - var cb = new ConcurrentBag(); - var cts = new CancellationTokenSource(); - - await Assert.ThrowsAsync(async () => - { - await ParallelFactory.ForAsync(0, count, async (i, ct) => - { - if (i > 500) { cts.Cancel(); } - await Task.Delay(Generate.RandomNumber(25, 75), ct); - cb.Add(i); - }, o => o.CancellationToken = cts.Token); - }); - - TestOutput.WriteLine($"Threads processed: {cb.Count}."); - - Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); - } - - [Fact] - public async Task ForAsync_ShouldRunConcurrent_LongRunning_SystemPartition() - { - var count = sbyte.MaxValue; - var expected = Generate.RangeOf(count, i => i); - var cb = new ConcurrentBag(); - - await ParallelFactory.ForAsync(0, count, async (i, ct) => - { - await Task.Delay(1000, ct); - cb.Add(i); - }); - - Assert.Equal(count, cb.Count); - Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); - } - - [Fact] - public async Task ForAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() - { - var count = short.MaxValue; - var expected = Generate.RangeOf(count, i => i); - var cb = new ConcurrentBag(); - - await ParallelFactory.ForAsync(0, count, async (i, ct) => - { - await Task.Delay(100, ct); - cb.Add(i); - }, o => o.PartitionSize = 4096); - - Assert.Equal(count, cb.Count); - Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); - } - } -} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs deleted file mode 100644 index b1ec9dae2..000000000 --- a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.Extensions.Xunit; -using Xunit; -using Xunit.Abstractions; - -namespace Cuemon.Threading -{ - public class ParallelFactoryTest : Test - { - public ParallelFactoryTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void For_ShouldRunConcurrent() - { - var atMostExpectedCount = 500; - var count = 1000; - var expected = Generate.RangeOf(count, i => i); - var cb = new ConcurrentBag(); - - ParallelFactory.For(0, count, i => - { - Thread.Sleep(50); - cb.Add(i); - }, o => o.CreationOptions = TaskCreationOptions.None); - - Assert.Equal(count, cb.Count); - Assert.True(expected.SequenceEqual(cb.OrderBy(i => i))); - } - - [Fact] - public void For_ShouldRunConcurrent_IgniteCancellation() - { - var count = 1000; - var cb = new ConcurrentBag(); - var cts = new CancellationTokenSource(); - var x = 0; - Assert.Throws(() => - { - ParallelFactory.For(0, count, i => - { - Interlocked.Increment(ref x); - if (i > 500) { cts.Cancel(); } - Thread.Sleep(Generate.RandomNumber(25, 75)); - cb.Add(i); - }, o => - { - o.CancellationToken = cts.Token; - o.CreationOptions = TaskCreationOptions.None; - }); - }); - - Thread.Sleep(500); // wait for possible background threads being canceled - - TestOutput.WriteLine(x.ToString()); - TestOutput.WriteLine($"Threads processed: {cb.Count}."); - - Assert.InRange(cb.Count, 300, 600); // most threads should have executed before cancellation - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); - } - - [Fact] - public void For_ShouldRunConcurrent_LongRunning_SystemPartition() - { - var count = sbyte.MaxValue; - var expected = Generate.RangeOf(count, i => i); - var cb = new ConcurrentBag(); - - ParallelFactory.For(0, count, i => - { - Thread.Sleep(1000); - cb.Add(i); - }); - - Assert.Equal(count, cb.Count); - Assert.True(expected.SequenceEqual(cb.OrderBy(i => i))); - } - - [Fact] - public void For_ShouldRunConcurrent_LongRunning_ExtremePartition() - { - var count = short.MaxValue; - var expected = Generate.RangeOf(count, i => i); - var cb = new ConcurrentBag(); - - ParallelFactory.For(0, count, i => - { - Thread.Sleep(100); - cb.Add(i); - }, o => o.PartitionSize = 4096); - - Assert.Equal(count, cb.Count); - Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); - } - - - } -} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/WhileAsyncTest.cs b/test/Cuemon.Threading.Tests/WhileAsyncTest.cs index 3f8722571..3c6eb4f26 100644 --- a/test/Cuemon.Threading.Tests/WhileAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/WhileAsyncTest.cs @@ -1,4 +1,6 @@ using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Cuemon.Extensions.Xunit; @@ -18,7 +20,7 @@ public async Task WhileAsyncTest_ShouldRunOn1000Threads() { var cb = new ConcurrentBag(); var fakeReader = new ConcurrentQueue(Generate.RangeOf(1000, i => i)); - await AdvancedParallelFactory.WhileAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => cq.TryDequeue(out var x), i => + await ParallelFactory.WhileAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => cq.TryDequeue(out var x), i => { Thread.Sleep(50); // todo: refactor to true async method cb.Add(Thread.CurrentThread.ManagedThreadId); diff --git a/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs b/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs index d27c4ba97..9dd383c5b 100644 --- a/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs @@ -19,7 +19,7 @@ public async Task WhileResultAsyncTest_ShouldRunOn1000Threads() { var cb = new ConcurrentBag(); var fakeReader = new ConcurrentQueue(Generate.RangeOf(1000, i => i)); - var result = await AdvancedParallelFactory.WhileResultAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => + var result = await ParallelFactory.WhileResultAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => { if (cq.TryDequeue(out var x)) { From 0ac8b989c309ee574ce60e9d2e13847f9d27f084 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 9 Sep 2020 03:06:31 +0200 Subject: [PATCH 162/385] Phrasing. --- src/Cuemon.Resilience/TransientOperation.Async.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Cuemon.Resilience/TransientOperation.Async.cs b/src/Cuemon.Resilience/TransientOperation.Async.cs index f1349566d..eb5856ac6 100644 --- a/src/Cuemon.Resilience/TransientOperation.Async.cs +++ b/src/Cuemon.Resilience/TransientOperation.Async.cs @@ -10,7 +10,7 @@ public static partial class TransientOperation /// Repetitively executes the specified until the operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// /// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The token to monitor for cancellation requests. The default value is . /// The which may be configured. /// The result from the . @@ -40,7 +40,7 @@ public static Task WithFuncAsync(Func /// The type of the parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The parameter of the function delegate . /// The token to monitor for cancellation requests. The default value is . /// The which may be configured. @@ -72,7 +72,7 @@ public static Task WithFuncAsync(FuncThe type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The token to monitor for cancellation requests. The default value is . @@ -106,7 +106,7 @@ public static Task WithFuncAsync(FuncThe type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . @@ -142,7 +142,7 @@ public static Task WithFuncAsync(FuncThe type of the third parameter of the function delegate . /// The type of the fourth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . @@ -180,7 +180,7 @@ public static Task WithFuncAsync(FuncThe type of the fourth parameter of the function delegate . /// The type of the fifth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The fault sensitive function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. + /// The fault sensitive based function delegate that is invoked until an operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to transient fault condition. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . From 7198df0334466198b1cb35275d786382f166ff5d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 10 Sep 2020 01:32:55 +0200 Subject: [PATCH 163/385] Unit tests for Cuemon.Threading. --- test/Cuemon.Threading.Tests/ForAsyncTest.cs | 29 - .../ForEachAsyncTest.cs | 31 - .../ForEachResultAsyncTest.cs | 39 -- .../ForResultAsyncTest.cs | 37 -- .../ParallelFactoryAsyncTest.cs | 483 ++++++++++++++++ .../ParallelFactoryTest.cs | 544 ++++++++++++++++++ test/Cuemon.Threading.Tests/WhileAsyncTest.cs | 32 -- .../WhileResultAsyncTest.cs | 46 -- 8 files changed, 1027 insertions(+), 214 deletions(-) delete mode 100644 test/Cuemon.Threading.Tests/ForAsyncTest.cs delete mode 100644 test/Cuemon.Threading.Tests/ForEachAsyncTest.cs delete mode 100644 test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs delete mode 100644 test/Cuemon.Threading.Tests/ForResultAsyncTest.cs create mode 100644 test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs create mode 100644 test/Cuemon.Threading.Tests/ParallelFactoryTest.cs delete mode 100644 test/Cuemon.Threading.Tests/WhileAsyncTest.cs delete mode 100644 test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs diff --git a/test/Cuemon.Threading.Tests/ForAsyncTest.cs b/test/Cuemon.Threading.Tests/ForAsyncTest.cs deleted file mode 100644 index c1efe28ff..000000000 --- a/test/Cuemon.Threading.Tests/ForAsyncTest.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Concurrent; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.Extensions.Xunit; -using Xunit; -using Xunit.Abstractions; - -namespace Cuemon.Threading -{ - public class ForAsyncTest : Test - { - public ForAsyncTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task ForAsync_ShouldRunOn1000Threads() - { - var cb = new ConcurrentBag(); - await ParallelFactory.ForAsync(0, 1000, i => - { - Thread.Sleep(50); // todo: refactor to true async method - cb.Add(Thread.CurrentThread.ManagedThreadId); - }, o => o.PartitionSize = 64); - - Assert.Equal(1000, cb.Count); - } - } -} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs b/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs deleted file mode 100644 index 994b72bf0..000000000 --- a/test/Cuemon.Threading.Tests/ForEachAsyncTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Concurrent; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.Extensions.Xunit; -using Xunit; -using Xunit.Abstractions; - -namespace Cuemon.Threading -{ - public class ForEachAsyncTest : Test - { - public ForEachAsyncTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task ForEachAsyncTest_ShouldRunOn1000Threads() - { - var ic = Generate.RangeOf(1000, i => i); - var cb = new ConcurrentBag(); - await ParallelFactory.ForEachAsync(ic, i => - { - Thread.Sleep(50); // todo: refactor to true async method - cb.Add(Thread.CurrentThread.ManagedThreadId); - }, o => o.PartitionSize = 64); - - Assert.Equal(1000, cb.Count); - } - } -} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs b/test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs deleted file mode 100644 index 509d2e46e..000000000 --- a/test/Cuemon.Threading.Tests/ForEachResultAsyncTest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Concurrent; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.Extensions.Xunit; -using Xunit; -using Xunit.Abstractions; - -namespace Cuemon.Threading -{ - public class ForEachResultAsyncTest : Test - { - public ForEachResultAsyncTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task ForEachResultAsyncTest_ShouldRunOn1000Threads() - { - var ic = Generate.RangeOf(1000, i => i); - var cb = new ConcurrentBag(); - var result = await ParallelFactory.ForEachResultAsync(ic, i => - { - Thread.Sleep(50); // todo: refactor to true async method - cb.Add(Thread.CurrentThread.ManagedThreadId); - return i; - }, o => o.PartitionSize = 64); - - Assert.Equal(1000, result.Count); - Assert.Equal(1000, result.Distinct().Count()); - Assert.Equal(0, result.Min()); - Assert.Equal(999, result.Max()); - Assert.Equal(0, result.First()); - Assert.Equal(999, result.Last()); - - Assert.Equal(1000, cb.Count); - } - } -} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ForResultAsyncTest.cs b/test/Cuemon.Threading.Tests/ForResultAsyncTest.cs deleted file mode 100644 index e02e50368..000000000 --- a/test/Cuemon.Threading.Tests/ForResultAsyncTest.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Concurrent; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.Extensions.Xunit; -using Xunit; -using Xunit.Abstractions; - -namespace Cuemon.Threading -{ - public class ForResultAsyncTest : Test - { - public ForResultAsyncTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task ForResultAsyncTest_ShouldRunOn1000Threads() - { - var cb = new ConcurrentBag(); - var result = await ParallelFactory.ForResultAsync(0, 1000, i => { - Thread.Sleep(50); // todo: refactor to true async method - cb.Add(Thread.CurrentThread.ManagedThreadId); - return i; - }, o => o.PartitionSize = 64); - - Assert.Equal(1000, result.Count); - Assert.Equal(1000, result.Distinct().Count()); - Assert.Equal(0, result.Min()); - Assert.Equal(999, result.Max()); - Assert.Equal(0, result.First()); - Assert.Equal(999, result.Last()); - - Assert.Equal(1000, cb.Count); - } - } -} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs new file mode 100644 index 000000000..895583968 --- /dev/null +++ b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs @@ -0,0 +1,483 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Threading +{ + public class ParallelFactoryAsyncTest : Test + { + public ParallelFactoryAsyncTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ForAsync_ShouldRunConcurrent() + { + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + await ParallelFactory.ForAsync(0, count, async (i, ct) => + { + await Task.Delay(50, ct); + cb.Add(i); + }, o => o.PartitionSize = 64); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForAsync_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + + await Assert.ThrowsAsync(async () => + { + await ParallelFactory.ForAsync(0, count, async (i, ct) => + { + if (i > 450) { cts.Cancel(); } + await Task.Delay(Generate.RandomNumber(25, 75), ct); + cb.Add(i); + }, o => o.CancellationToken = cts.Token); + }); + + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForAsync_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + await ParallelFactory.ForAsync(0, count, async (i, ct) => + { + await Task.Delay(1000, ct); + cb.Add(i); + }); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + await ParallelFactory.ForAsync(0, count, async (i, ct) => + { + await Task.Delay(100, ct); + cb.Add(i); + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForResultAsync_ShouldRunConcurrent() + { + var count = 1000; + var cb = new ConcurrentBag(); + + var result = await ParallelFactory.ForResultAsync(0, count, async (i, ct) => + { + await Task.Delay(50, ct); + cb.Add(i); + return i; + }, o => o.PartitionSize = 64); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForResultAsync_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + + await Assert.ThrowsAsync(async () => + { + await ParallelFactory.ForResultAsync(0, count, async (i, ct) => + { + if (i > 450) { cts.Cancel(); } + await Task.Delay(Generate.RandomNumber(25, 75), ct); + cb.Add(i); + return i; + }, o => o.CancellationToken = cts.Token); + }); + + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForResultAsync_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var cb = new ConcurrentBag(); + + var result = await ParallelFactory.ForResultAsync(0, count, async (i, ct) => + { + await Task.Delay(1000, ct); + cb.Add(i); + return i; + }); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForResultAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var cb = new ConcurrentBag(); + + var result = await ParallelFactory.ForResultAsync(0, count, async (i, ct) => + { + await Task.Delay(100, ct); + cb.Add(i); + return i; + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForEachAsync_ShouldRunConcurrent() + { + var count = 1000; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + await ParallelFactory.ForEachAsync(ic, async (i, ct) => + { + await Task.Delay(50, ct); + cb.Add(i); + }, o => o.PartitionSize = 64); + + Assert.Equal(count, cb.Count); + Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForEachAsync_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + + await Assert.ThrowsAsync(async () => + { + await ParallelFactory.ForEachAsync(ic, async (i, ct) => + { + if (i > 450) { cts.Cancel(); } + await Task.Delay(Generate.RandomNumber(25, 75), ct); + cb.Add(i); + }, o => o.CancellationToken = cts.Token); + }); + + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForEachAsync_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + await ParallelFactory.ForEachAsync(ic, async (i, ct) => + { + await Task.Delay(1000, ct); + cb.Add(i); + }); + + Assert.Equal(count, cb.Count); + Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForEachAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + await ParallelFactory.ForEachAsync(ic, async (i, ct) => + { + await Task.Delay(100, ct); + cb.Add(i); + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForEachResultAsync_ShouldRunConcurrent() + { + var count = 1000; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + var result = await ParallelFactory.ForEachResultAsync(ic, async (i, ct) => + { + await Task.Delay(50, ct); + cb.Add(i); + return i; + }, o => o.PartitionSize = 64); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForEachResultAsync_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + + await Assert.ThrowsAsync(async () => + { + await ParallelFactory.ForEachResultAsync(ic, async (i, ct) => + { + if (i > 450) { cts.Cancel(); } + await Task.Delay(Generate.RandomNumber(25, 75), ct); + cb.Add(i); + return i; + }, o => o.CancellationToken = cts.Token); + }); + + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForEachResultAsync_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + var result = await ParallelFactory.ForEachResultAsync(ic, async (i, ct) => + { + await Task.Delay(1000, ct); + cb.Add(i); + return i; + }); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task ForEachResultAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + var result = await ParallelFactory.ForEachResultAsync(ic, async (i, ct) => + { + await Task.Delay(100, ct); + cb.Add(i); + return i; + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task WhileAsync_ShouldRunConcurrent() + { + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => + { + await Task.Delay(50, ct); + cb.Add(i); + }, o => o.PartitionSize = 64); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task WhileAsync_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + + await Assert.ThrowsAsync(async () => + { + await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => + { + if (i > 450) { cts.Cancel(); } + await Task.Delay(Generate.RandomNumber(25, 75), ct); + cb.Add(i); + }, o => o.CancellationToken = cts.Token); + }); + + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task WhileAsync_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => + { + await Task.Delay(1000, ct); + cb.Add(i); + }); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task WhileAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => + { + await Task.Delay(100, ct); + cb.Add(i); + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task WhileResultAsync_ShouldRunConcurrent() + { + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + var result = await AdvancedParallelFactory.WhileResultAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => + { + await Task.Delay(50, ct); + cb.Add(i); + return i; + }, o => o.PartitionSize = 1); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task WhileResultAsync_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + + await Assert.ThrowsAsync(async () => + { + await AdvancedParallelFactory.WhileResultAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => + { + if (i > 450) { cts.Cancel(); } + await Task.Delay(Generate.RandomNumber(25, 75), ct); + cb.Add(i); + return i; + }, o => o.CancellationToken = cts.Token); + }); + + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task WhileResultAsync_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + var result = await AdvancedParallelFactory.WhileResultAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => + { + await Task.Delay(1000, ct); + cb.Add(i); + return i; + }); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public async Task WhileResultAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + var result = await AdvancedParallelFactory.WhileResultAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => + { + await Task.Delay(100, ct); + cb.Add(i); + return i; + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs new file mode 100644 index 000000000..89597d1ca --- /dev/null +++ b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs @@ -0,0 +1,544 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Threading +{ + public class ParallelFactoryTest : Test + { + public ParallelFactoryTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void For_ShouldRunConcurrent() + { + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + ParallelFactory.For(0, count, i => + { + Thread.Sleep(50); + cb.Add(i); + }, o => o.CreationOptions = TaskCreationOptions.None); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void For_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + var x = 0; + var ae = Assert.Throws(() => + { + ParallelFactory.For(0, count, i => + { + Interlocked.Increment(ref x); + if (i > 450) { cts.Cancel(); } + Thread.Sleep(Generate.RandomNumber(25, 75)); + cb.Add(i); + }, o => + { + o.CancellationToken = cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); + }); + + Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); + + Thread.Sleep(500); // wait for possible background threads being canceled + + TestOutput.WriteLine(x.ToString()); + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void For_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + ParallelFactory.For(0, count, i => + { + Thread.Sleep(1000); + cb.Add(i); + }); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void For_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + ParallelFactory.For(0, count, i => + { + Thread.Sleep(1); + cb.Add(i); + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForResult_ShouldRunConcurrent() + { + var count = 1000; + var cb = new ConcurrentBag(); + + var result = ParallelFactory.ForResult(0, count, i => + { + Thread.Sleep(50); + cb.Add(i); + return i; + }, o => o.CreationOptions = TaskCreationOptions.None); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForResult_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + var x = 0; + var ae = Assert.Throws(() => + { + ParallelFactory.ForResult(0, count, i => + { + Interlocked.Increment(ref x); + if (i > 450) { cts.Cancel(); } + Thread.Sleep(Generate.RandomNumber(25, 75)); + cb.Add(i); + return i; + }, o => + { + o.CancellationToken = cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); + }); + + Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); + + Thread.Sleep(500); // wait for possible background threads being canceled + + TestOutput.WriteLine(x.ToString()); + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForResult_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var cb = new ConcurrentBag(); + + var result = ParallelFactory.ForResult(0, count, i => + { + Thread.Sleep(1000); + cb.Add(i); + return i; + }); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForResult_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var cb = new ConcurrentBag(); + + var result = ParallelFactory.ForResult(0, count, i => + { + Thread.Sleep(100); + cb.Add(i); + return i; + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForEach_ShouldRunConcurrent() + { + var count = 1000; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + ParallelFactory.ForEach(ic, i => + { + Thread.Sleep(50); + cb.Add(i); + }, o => o.CreationOptions = TaskCreationOptions.None); + + Assert.Equal(count, cb.Count); + Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForEach_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + var x = 0; + var ae = Assert.Throws(() => + { + ParallelFactory.ForEach(ic, i => + { + Interlocked.Increment(ref x); + if (i > 450) { cts.Cancel(); } + Thread.Sleep(Generate.RandomNumber(25, 75)); + cb.Add(i); + }, o => + { + o.CancellationToken = cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); + }); + + Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); + + Thread.Sleep(500); // wait for possible background threads being canceled + + TestOutput.WriteLine(x.ToString()); + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForEach_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + ParallelFactory.ForEach(ic, i => + { + Thread.Sleep(1000); + cb.Add(i); + }); + + Assert.Equal(count, cb.Count); + Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForEach_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + ParallelFactory.ForEach(ic, i => + { + Thread.Sleep(100); + cb.Add(i); + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForEachResult_ShouldRunConcurrent() + { + var count = 1000; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + var result = ParallelFactory.ForEachResult(ic, i => + { + Thread.Sleep(50); + cb.Add(i); + return i; + }, o => o.CreationOptions = TaskCreationOptions.None); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForEachResult_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + var x = 0; + var ae = Assert.Throws(() => + { + ParallelFactory.ForEachResult(ic, i => + { + Interlocked.Increment(ref x); + if (i > 450) { cts.Cancel(); } + Thread.Sleep(Generate.RandomNumber(25, 75)); + cb.Add(i); + return i; + }, o => + { + o.CancellationToken = cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); + }); + + Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); + + Thread.Sleep(500); // wait for possible background threads being canceled + + TestOutput.WriteLine(x.ToString()); + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForEachResult_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + var result = ParallelFactory.ForEachResult(ic, i => + { + Thread.Sleep(1000); + cb.Add(i); + return i; + }); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void ForEachResult_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var ic = Generate.RangeOf(count, i => i); + var cb = new ConcurrentBag(); + + var result = ParallelFactory.ForEachResult(ic, i => + { + Thread.Sleep(100); + cb.Add(i); + return i; + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void While_ShouldRunConcurrent() + { + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + AdvancedParallelFactory.While(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => + { + Thread.Sleep(50); + cb.Add(i); + }, o => o.CreationOptions = TaskCreationOptions.None); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void While_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + var x = 0; + var ae = Assert.Throws(() => + { + AdvancedParallelFactory.While(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => + { + Interlocked.Increment(ref x); + if (i > 450) { cts.Cancel(); } + Thread.Sleep(Generate.RandomNumber(25, 75)); + cb.Add(i); + }, o => + { + o.CancellationToken = cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); + }); + + Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); + + Thread.Sleep(500); // wait for possible background threads being canceled + + TestOutput.WriteLine(x.ToString()); + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void While_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + AdvancedParallelFactory.While(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => + { + Thread.Sleep(1000); + cb.Add(i); + }); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void While_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + AdvancedParallelFactory.While(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => + { + Thread.Sleep(100); + cb.Add(i); + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void WhileResult_ShouldRunConcurrent() + { + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + var result = AdvancedParallelFactory.WhileResult(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => + { + Thread.Sleep(50); + cb.Add(i); + return i; + }, o => o.CreationOptions = TaskCreationOptions.None); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void WhileResult_ShouldRunConcurrent_IgniteCancellation() + { + var count = 1000; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + var cts = new CancellationTokenSource(); + var x = 0; + var ae = Assert.Throws(() => + { + AdvancedParallelFactory.WhileResult(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => + { + Interlocked.Increment(ref x); + if (i > 450) { cts.Cancel(); } + Thread.Sleep(Generate.RandomNumber(25, 75)); + cb.Add(i); + return i; + }, o => + { + o.CancellationToken = cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); + }); + + Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); + + Thread.Sleep(500); // wait for possible background threads being canceled + + TestOutput.WriteLine(x.ToString()); + TestOutput.WriteLine($"Threads processed: {cb.Count}."); + + Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void WhileResult_ShouldRunConcurrent_LongRunning_SystemPartition() + { + var count = sbyte.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + var result = AdvancedParallelFactory.WhileResult(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => + { + Thread.Sleep(1000); + cb.Add(i); + return i; + }); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + + [Fact] + public void WhileResult_ShouldRunConcurrent_LongRunning_ExtremePartition() + { + var count = short.MaxValue; + var expected = Generate.RangeOf(count, i => i); + var ic = new Queue(expected); + var cb = new ConcurrentBag(); + + var result = AdvancedParallelFactory.WhileResult(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => + { + Thread.Sleep(100); + cb.Add(i); + return i; + }, o => o.PartitionSize = 4096); + + Assert.Equal(count, cb.Count); + Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/WhileAsyncTest.cs b/test/Cuemon.Threading.Tests/WhileAsyncTest.cs deleted file mode 100644 index 3c6eb4f26..000000000 --- a/test/Cuemon.Threading.Tests/WhileAsyncTest.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.Extensions.Xunit; -using Xunit; -using Xunit.Abstractions; - -namespace Cuemon.Threading -{ - public class WhileAsyncTest : Test - { - public WhileAsyncTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task WhileAsyncTest_ShouldRunOn1000Threads() - { - var cb = new ConcurrentBag(); - var fakeReader = new ConcurrentQueue(Generate.RangeOf(1000, i => i)); - await ParallelFactory.WhileAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => cq.TryDequeue(out var x), i => - { - Thread.Sleep(50); // todo: refactor to true async method - cb.Add(Thread.CurrentThread.ManagedThreadId); - }, o => o.PartitionSize = 64); - - Assert.Equal(1000, cb.Count); - } - } -} \ No newline at end of file diff --git a/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs b/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs deleted file mode 100644 index 9dd383c5b..000000000 --- a/test/Cuemon.Threading.Tests/WhileResultAsyncTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Concurrent; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.Extensions.Xunit; -using Xunit; -using Xunit.Abstractions; - -namespace Cuemon.Threading -{ - public class WhileResultAsyncTest : Test - { - public WhileResultAsyncTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task WhileResultAsyncTest_ShouldRunOn1000Threads() - { - var cb = new ConcurrentBag(); - var fakeReader = new ConcurrentQueue(Generate.RangeOf(1000, i => i)); - var result = await ParallelFactory.WhileResultAsync(fakeReader, () => Task.FromResult(fakeReader.TryPeek(out _)), cq => - { - if (cq.TryDequeue(out var x)) - { - return x; - } - return -1; - }, i => - { - Thread.Sleep(50); // todo: refactor to true async method - cb.Add(Thread.CurrentThread.ManagedThreadId); - return i; - }, o => o.PartitionSize = 64); - - Assert.Equal(1000, result.Count); - Assert.Equal(1000, result.Distinct().Count()); - Assert.Equal(0, result.Min()); - Assert.Equal(999, result.Max()); - Assert.Equal(0, result.First()); - Assert.Equal(999, result.Last()); - - Assert.Equal(1000, cb.Count); - } - } -} \ No newline at end of file From 3d16b7c747a5728366ec4a2c73d27fbb6788b986 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 00:37:47 +0200 Subject: [PATCH 164/385] Complete refactoring following clean code principles. Also extensive unit testing and fixed a few bugs. --- src/Cuemon.Core/ActionFactory.cs | 2 +- src/Cuemon.Core/FuncFactory.cs | 2 +- src/Cuemon.Core/TaskActionFactory.cs | 2 +- src/Cuemon.Core/TaskFuncFactory.cs | 2 +- src/Cuemon.Core/TemplateFactory.cs | 7 + src/Cuemon.Core/TesterFuncFactory.cs | 2 +- .../ActionForEachSynchronousLoop.cs | 22 ++ .../ActionForSynchronousLoop.cs | 16 ++ .../ActionWhileSynchronousLoop.cs | 17 ++ .../AdvancedParallelFactory.For.cs | 158 +++++++++++ .../AdvancedParallelFactory.ForAsync.cs | 187 +++++++++++++ .../AdvancedParallelFactory.ForResult.cs | 179 ++++++++++++ .../AdvancedParallelFactory.ForResultAsync.cs | 207 ++++++++++++++ .../AdvancedParallelFactory.While.cs | 151 +++++++++++ ... => AdvancedParallelFactory.WhileAsync.cs} | 74 ++--- .../AdvancedParallelFactory.WhileResult.cs | 164 +++++++++++ ...vancedParallelFactory.WhileResultAsync.cs} | 80 ++---- .../AdvancedParallelFactory.cs | 55 ++++ src/Cuemon.Threading/AsyncForwardIterator.cs | 34 +++ ...yOptions.cs => AsyncTaskFactoryOptions.cs} | 19 +- src/Cuemon.Threading/AsyncWorkloadOptions.cs | 37 +++ src/Cuemon.Threading/AsynchronousLoop.cs | 23 ++ .../ForEachSynchronousLoop.cs | 45 ++++ src/Cuemon.Threading/ForLoopRuleset.cs | 92 +++++++ src/Cuemon.Threading/ForSynchronousLoop.cs | 57 ++++ src/Cuemon.Threading/ForwardIterator.cs | 11 +- .../FuncForEachSynchronousLoop.cs | 32 +++ .../FuncForSynchronousLoop.cs | 33 +++ .../FuncWhileSynchronousLoop.cs | 32 +++ src/Cuemon.Threading/Loop.cs | 14 + src/Cuemon.Threading/ParallelFactory.For.cs | 223 +++++++++++++++ .../ParallelFactory.ForAsync.cs | 254 ++++-------------- .../ParallelFactory.ForEach.cs | 127 +++++++++ .../ParallelFactory.ForEachAsync.cs | 66 ++--- .../ParallelFactory.ForEachResult.cs | 142 ++++++++++ .../ParallelFactory.ForEachResultAsync.cs | 74 ++--- .../ParallelFactory.ForResult.cs | 248 +++++++++++++++++ .../ParallelFactory.ForResultAsync.cs | 218 ++++----------- src/Cuemon.Threading/ParallelFactory.cs | 9 + src/Cuemon.Threading/SynchronousLoop.cs | 56 ++++ src/Cuemon.Threading/WhileSynchronousLoop.cs | 58 ++++ .../Http/UriExtensionsTest.cs | 4 +- .../ParallelFactoryTest.cs | 53 +++- 43 files changed, 2700 insertions(+), 588 deletions(-) create mode 100644 src/Cuemon.Threading/ActionForEachSynchronousLoop.cs create mode 100644 src/Cuemon.Threading/ActionForSynchronousLoop.cs create mode 100644 src/Cuemon.Threading/ActionWhileSynchronousLoop.cs create mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.For.cs create mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs create mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.ForResult.cs create mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs create mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.While.cs rename src/Cuemon.Threading/{ParallelFactory.WhileAsync.cs => AdvancedParallelFactory.WhileAsync.cs} (75%) create mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.WhileResult.cs rename src/Cuemon.Threading/{ParallelFactory.WhileResultAsync.cs => AdvancedParallelFactory.WhileResultAsync.cs} (79%) create mode 100644 src/Cuemon.Threading/AdvancedParallelFactory.cs create mode 100644 src/Cuemon.Threading/AsyncForwardIterator.cs rename src/Cuemon.Threading/{TaskFactoryOptions.cs => AsyncTaskFactoryOptions.cs} (72%) create mode 100644 src/Cuemon.Threading/AsyncWorkloadOptions.cs create mode 100644 src/Cuemon.Threading/AsynchronousLoop.cs create mode 100644 src/Cuemon.Threading/ForEachSynchronousLoop.cs create mode 100644 src/Cuemon.Threading/ForLoopRuleset.cs create mode 100644 src/Cuemon.Threading/ForSynchronousLoop.cs create mode 100644 src/Cuemon.Threading/FuncForEachSynchronousLoop.cs create mode 100644 src/Cuemon.Threading/FuncForSynchronousLoop.cs create mode 100644 src/Cuemon.Threading/FuncWhileSynchronousLoop.cs create mode 100644 src/Cuemon.Threading/Loop.cs create mode 100644 src/Cuemon.Threading/ParallelFactory.For.cs create mode 100644 src/Cuemon.Threading/ParallelFactory.ForEach.cs create mode 100644 src/Cuemon.Threading/ParallelFactory.ForEachResult.cs create mode 100644 src/Cuemon.Threading/ParallelFactory.ForResult.cs create mode 100644 src/Cuemon.Threading/ParallelFactory.cs create mode 100644 src/Cuemon.Threading/SynchronousLoop.cs create mode 100644 src/Cuemon.Threading/WhileSynchronousLoop.cs diff --git a/src/Cuemon.Core/ActionFactory.cs b/src/Cuemon.Core/ActionFactory.cs index 3bdc44bfa..c194a33dd 100644 --- a/src/Cuemon.Core/ActionFactory.cs +++ b/src/Cuemon.Core/ActionFactory.cs @@ -509,7 +509,7 @@ public void ExecuteMethod() /// /// A new that is a copy of this instance. /// When thread safety is required this is the method to invoke. - public ActionFactory Clone() + public override TemplateFactory Clone() { return new ActionFactory(Method, GenericArguments.Clone() as TTuple); } diff --git a/src/Cuemon.Core/FuncFactory.cs b/src/Cuemon.Core/FuncFactory.cs index 14df4e07a..afb0d6b59 100644 --- a/src/Cuemon.Core/FuncFactory.cs +++ b/src/Cuemon.Core/FuncFactory.cs @@ -530,7 +530,7 @@ public TResult ExecuteMethod() /// /// A new that is a copy of this instance. /// When thread safety is required this is the method to invoke. - public FuncFactory Clone() + public override TemplateFactory Clone() { return new FuncFactory(Method, GenericArguments.Clone() as TTuple); } diff --git a/src/Cuemon.Core/TaskActionFactory.cs b/src/Cuemon.Core/TaskActionFactory.cs index d479d756f..6cc0fbded 100644 --- a/src/Cuemon.Core/TaskActionFactory.cs +++ b/src/Cuemon.Core/TaskActionFactory.cs @@ -460,7 +460,7 @@ public Task ExecuteMethodAsync(CancellationToken ct) /// /// A new that is a copy of this instance. /// When thread safety is required this is the method to invoke. - public TaskActionFactory Clone() + public override TemplateFactory Clone() { return new TaskActionFactory(Method, GenericArguments.Clone() as TTuple); } diff --git a/src/Cuemon.Core/TaskFuncFactory.cs b/src/Cuemon.Core/TaskFuncFactory.cs index bdee522e0..62ef4511a 100644 --- a/src/Cuemon.Core/TaskFuncFactory.cs +++ b/src/Cuemon.Core/TaskFuncFactory.cs @@ -477,7 +477,7 @@ public Task ExecuteMethodAsync(CancellationToken ct) /// /// A new that is a copy of this instance. /// When thread safety is required this is the method to invoke. - public TaskFuncFactory Clone() + public override TemplateFactory Clone() { return new TaskFuncFactory(Method, GenericArguments.Clone() as TTuple); } diff --git a/src/Cuemon.Core/TemplateFactory.cs b/src/Cuemon.Core/TemplateFactory.cs index 043a4fc99..7db5c9124 100644 --- a/src/Cuemon.Core/TemplateFactory.cs +++ b/src/Cuemon.Core/TemplateFactory.cs @@ -62,5 +62,12 @@ protected void ThrowIfNoValidDelegate(bool delegateIsNull) { if (!HasDelegate) { throw new InvalidOperationException(delegateIsNull ? "There is no delegate specified on the factory." : FormattableString.Invariant($"There is a delegate specified on the factory, '{Decorator.Enclose(GetType()).ToFriendlyName(o => o.FullName = true)}', but it leads to a null referenced delegate wrapper.")); } } + + /// + /// Creates a shallow copy of the current object. + /// + /// A new implementation that is a copy of this instance. + /// When thread safety is required this is the method to invoke. + public abstract TemplateFactory Clone(); } } \ No newline at end of file diff --git a/src/Cuemon.Core/TesterFuncFactory.cs b/src/Cuemon.Core/TesterFuncFactory.cs index 29e6d8170..68cb80c84 100644 --- a/src/Cuemon.Core/TesterFuncFactory.cs +++ b/src/Cuemon.Core/TesterFuncFactory.cs @@ -563,7 +563,7 @@ public virtual TSuccess ExecuteMethod(out TResult result) /// /// A new that is a copy of this instance. /// When thread safety is required this is the method to invoke. - public TesterFuncFactory Clone() + public override TemplateFactory Clone() { return new TesterFuncFactory(Method, GenericArguments.Clone() as TTuple); } diff --git a/src/Cuemon.Threading/ActionForEachSynchronousLoop.cs b/src/Cuemon.Threading/ActionForEachSynchronousLoop.cs new file mode 100644 index 000000000..259dbd9e0 --- /dev/null +++ b/src/Cuemon.Threading/ActionForEachSynchronousLoop.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using Cuemon.Collections.Generic; + +namespace Cuemon.Threading +{ + internal sealed class ActionForEachSynchronousLoop : ForEachSynchronousLoop + { + public ActionForEachSynchronousLoop(IEnumerable source, Action setup) : base(source, setup) + { + Partitioner = new PartitionerEnumerable(source, Options.PartitionSize); + WhileCondition = () => Partitioner.HasPartitions; + } + + private PartitionerEnumerable Partitioner { get; set; } + + protected override void FillWorkQueueWorkerFactory(TemplateFactory worker, long sorter) + { + if (worker is ActionFactory wf) { wf.ExecuteMethod(); } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ActionForSynchronousLoop.cs b/src/Cuemon.Threading/ActionForSynchronousLoop.cs new file mode 100644 index 000000000..3bd0dc6e2 --- /dev/null +++ b/src/Cuemon.Threading/ActionForSynchronousLoop.cs @@ -0,0 +1,16 @@ +using System; + +namespace Cuemon.Threading +{ + internal sealed class ActionForSynchronousLoop : ForSynchronousLoop where TSource : struct, IComparable, IEquatable, IConvertible + { + public ActionForSynchronousLoop(ForLoopRuleset rules, Action setup) : base(rules, setup) + { + } + + protected override void FillWorkQueueWorkerFactory(TemplateFactory worker) + { + if (worker is ActionFactory wf) { wf.ExecuteMethod(); } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ActionWhileSynchronousLoop.cs b/src/Cuemon.Threading/ActionWhileSynchronousLoop.cs new file mode 100644 index 000000000..dd831f0bd --- /dev/null +++ b/src/Cuemon.Threading/ActionWhileSynchronousLoop.cs @@ -0,0 +1,17 @@ +using System; + +namespace Cuemon.Threading +{ + internal sealed class ActionWhileSynchronousLoop : WhileSynchronousLoop + { + public ActionWhileSynchronousLoop(ForwardIterator iterator, Action setup) : base(iterator, setup) + { + } + + + protected override void FillWorkQueueWorkerFactory(TemplateFactory worker, long sorter) + { + if (worker is ActionFactory wf) { wf.ExecuteMethod(); } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.For.cs b/src/Cuemon.Threading/AdvancedParallelFactory.For.cs new file mode 100644 index 000000000..84fcefbb3 --- /dev/null +++ b/src/Cuemon.Threading/AdvancedParallelFactory.For.cs @@ -0,0 +1,158 @@ +using System; + +namespace Cuemon.Threading +{ + public static partial class AdvancedParallelFactory + { + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The which may be configured. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static void For(ForLoopRuleset rules, Action worker, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + ForCore(rules, ActionFactory.Create(worker, default), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the parameter of the delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The parameter of the delegate . + /// The which may be configured. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static void For(ForLoopRuleset rules, Action worker, T arg, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + ForCore(rules, ActionFactory.Create(worker, default, arg), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The which may be configured. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static void For(ForLoopRuleset rules, Action worker, T1 arg1, T2 arg2, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + ForCore(rules, ActionFactory.Create(worker, default, arg1, arg2), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The which may be configured. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static void For(ForLoopRuleset rules, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + ForCore(rules, ActionFactory.Create(worker, default, arg1, arg2, arg3), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The which may be configured. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static void For(ForLoopRuleset rules, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + ForCore(rules, ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The fifth parameter of the delegate . + /// The which may be configured. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static void For(ForLoopRuleset rules, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(worker, nameof(worker)); + Validator.ThrowIfNull(rules, nameof(rules)); + ForCore(rules, ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); + } + + private static void ForCore(ForLoopRuleset rules, ActionFactory workerFactory, Action setup) + where TWorker : Template + where TOperand : struct, IComparable, IEquatable, IConvertible + { + new ActionForSynchronousLoop(rules, setup).PrepareExecution(workerFactory); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs new file mode 100644 index 000000000..37be592e6 --- /dev/null +++ b/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + public static partial class AdvancedParallelFactory + { + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The rules of a for-loop control flow statement. + /// The based function delegate that is invoked once per iteration. + /// The which may be configured. + /// A that represents the asynchronous operation. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task ForAsync(ForLoopRuleset rules, Func worker, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(rules, TaskActionFactory.Create(worker, default), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the parameter of the delegate . + /// The rules of a for-loop control flow statement. + /// The based function delegate that is invoked once per iteration. + /// The parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task ForAsync(ForLoopRuleset rules, Func worker, T arg, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(rules, TaskActionFactory.Create(worker, default, arg), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The rules of a for-loop control flow statement. + /// The based function delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task ForAsync(ForLoopRuleset rules, Func worker, T1 arg1, T2 arg2, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(rules, TaskActionFactory.Create(worker, default, arg1, arg2), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The rules of a for-loop control flow statement. + /// The based function delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task ForAsync(ForLoopRuleset rules, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(rules, TaskActionFactory.Create(worker, default, arg1, arg2, arg3), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The rules of a for-loop control flow statement. + /// The based function delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task ForAsync(ForLoopRuleset rules, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(rules, TaskActionFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The rules of a for-loop control flow statement. + /// The based function delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The fifth parameter of the delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task ForAsync(ForLoopRuleset rules, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForCoreAsync(rules, TaskActionFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); + } + + private static async Task ForCoreAsync(ForLoopRuleset rules, TaskActionFactory workerFactory, Action setup) + where TWorker : Template + where TOperand : struct, IComparable, IEquatable, IConvertible + { + var from = rules.From; + var options = Patterns.Configure(setup); + TOperand processed = default; + while (true) + { + var workChunks = options.PartitionSize; + var queue = new List(); + for (var i = from; rules.Condition(i, rules.Relation, rules.To); i = rules.Iterator(i, rules.Assignment, rules.Step)) + { + workerFactory.GenericArguments.Arg1 = i; + queue.Add(workerFactory.ExecuteMethodAsync(options.CancellationToken)); + + processed = i; + workChunks--; + + if (workChunks == 0) { break; } + } + from = Calculator.Calculate(processed, rules.Assignment, rules.Step); + if (queue.Count == 0) { break; } + await Task.WhenAll(queue).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.ForResult.cs b/src/Cuemon.Threading/AdvancedParallelFactory.ForResult.cs new file mode 100644 index 000000000..da115a413 --- /dev/null +++ b/src/Cuemon.Threading/AdvancedParallelFactory.ForResult.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + public static partial class AdvancedParallelFactory + { + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IReadOnlyCollection ForResult(ForLoopRuleset rules, Func worker, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCore(rules, FuncFactory.Create(worker, default), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The parameter of the function delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IReadOnlyCollection ForResult(ForLoopRuleset rules, Func worker, T arg, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCore(rules, FuncFactory.Create(worker, default, arg), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IReadOnlyCollection ForResult(ForLoopRuleset rules, Func worker, T1 arg1, T2 arg2, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCore(rules, FuncFactory.Create(worker, default, arg1, arg2), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IReadOnlyCollection ForResult(ForLoopRuleset rules, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCore(rules, FuncFactory.Create(worker, default, arg1, arg2, arg3), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IReadOnlyCollection ForResult(ForLoopRuleset rules, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCore(rules, FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the fifth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The fifth parameter of the function delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IReadOnlyCollection ForResult(ForLoopRuleset rules, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCore(rules, FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); + } + + private static IReadOnlyCollection ForResultCore(ForLoopRuleset rules, FuncFactory workerFactory, Action setup) + where TWorker : Template + where TOperand : struct, IComparable, IEquatable, IConvertible + { + return new FuncForSynchronousLoop(rules, setup).GetResult(workerFactory); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs new file mode 100644 index 000000000..bce529ec6 --- /dev/null +++ b/src/Cuemon.Threading/AdvancedParallelFactory.ForResultAsync.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + public static partial class AdvancedParallelFactory + { + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task> ForResultAsync(ForLoopRuleset rules, Func> worker, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCoreAsync(rules, TaskFuncFactory.Create(worker, default), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The parameter of the function delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task> ForResultAsync(ForLoopRuleset rules, Func> worker, T arg, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCoreAsync(rules, TaskFuncFactory.Create(worker, default, arg), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task> ForResultAsync(ForLoopRuleset rules, Func> worker, T1 arg1, T2 arg2, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCoreAsync(rules, TaskFuncFactory.Create(worker, default, arg1, arg2), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task> ForResultAsync(ForLoopRuleset rules, Func> worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCoreAsync(rules, TaskFuncFactory.Create(worker, default, arg1, arg2, arg3), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task> ForResultAsync(ForLoopRuleset rules, Func> worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCoreAsync(rules, TaskFuncFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); + } + + /// + /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the number used with the loop control variable. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the fifth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The rules of a for-loop control flow statement. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The fifth parameter of the function delegate . + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static Task> ForResultAsync(ForLoopRuleset rules, Func> worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + where TOperand : struct, IComparable, IEquatable, IConvertible + { + Validator.ThrowIfNull(rules, nameof(rules)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForResultCoreAsync(rules, TaskFuncFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); + } + + private static async Task> ForResultCoreAsync(ForLoopRuleset rules, TaskFuncFactory workerFactory, Action setup) + where TWorker : Template + where TOperand : struct, IComparable, IEquatable, IConvertible + { + var from = rules.From; + var options = Patterns.Configure(setup); + var result = new ConcurrentDictionary(); + + TOperand processed = default; + while (true) + { + var workChunks = options.PartitionSize; + var queue = new Dictionary>(); + for (var i = from; rules.Condition(i, rules.Relation, rules.To); i = rules.Iterator(i, rules.Assignment, rules.Step)) + { + workerFactory.GenericArguments.Arg1 = i; + queue.Add(i, workerFactory.ExecuteMethodAsync(options.CancellationToken)); + + processed = i; + workChunks--; + + if (workChunks == 0) { break; } + } + from = Calculator.Calculate(processed, rules.Assignment, rules.Step); + if (queue.Count == 0) { break; } + await Task.WhenAll(queue.Values).ConfigureAwait(false); + foreach (var item in queue) { result.TryAdd(item.Key, item.Value.Result); } + } + return new ReadOnlyCollection(result.Values.ToList()); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.While.cs b/src/Cuemon.Threading/AdvancedParallelFactory.While.cs new file mode 100644 index 000000000..4d5ec1db8 --- /dev/null +++ b/src/Cuemon.Threading/AdvancedParallelFactory.While.cs @@ -0,0 +1,151 @@ +using System; + +namespace Cuemon.Threading +{ + public static partial class AdvancedParallelFactory + { + /// + /// Executes a parallel while loop. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The which may be configured. + public static void While(TReader reader, Func condition, Func provider, Action worker, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + WhileCore(new ForwardIterator(reader, condition, provider), ActionFactory.Create(worker, default), setup); + } + + /// + /// Executes a parallel while loop. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The type of the parameter of the delegate . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The parameter of the delegate . + /// The which may be configured. + public static void While(TReader reader, Func condition, Func provider, Action worker, T arg, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + WhileCore(new ForwardIterator(reader, condition, provider), ActionFactory.Create(worker, default, arg), setup); + } + + /// + /// Executes a parallel while loop. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The which may be configured. + public static void While(TReader reader, Func condition, Func provider, Action worker, T1 arg1, T2 arg2, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + WhileCore(new ForwardIterator(reader, condition, provider), ActionFactory.Create(worker, default, arg1, arg2), setup); + } + + /// + /// Executes a parallel while loop. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The which may be configured. + public static void While(TReader reader, Func condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + WhileCore(new ForwardIterator(reader, condition, provider), ActionFactory.Create(worker, default, arg1, arg2, arg3), setup); + } + + /// + /// Executes a parallel while loop. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The which may be configured. + public static void While(TReader reader, Func condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + WhileCore(new ForwardIterator(reader, condition, provider), ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); + } + + /// + /// Executes a parallel while loop. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The fifth parameter of the delegate . + /// The which may be configured. + public static void While(TReader reader, Func condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + WhileCore(new ForwardIterator(reader, condition, provider), ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); + } + + private static void WhileCore(ForwardIterator iterator, ActionFactory workerFactory, Action setup) + where TWorker : Template + { + new ActionWhileSynchronousLoop(iterator, setup).PrepareExecution(workerFactory); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.WhileAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs similarity index 75% rename from src/Cuemon.Threading/ParallelFactory.WhileAsync.cs rename to src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs index 032cd47cb..e09f31ca5 100644 --- a/src/Cuemon.Threading/ParallelFactory.WhileAsync.cs +++ b/src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs @@ -1,15 +1,11 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace Cuemon.Threading { - /// - /// Provides a factory based way to encapsulate and re-use existing code while adding support for typically long-running parallel loops and regions. - /// - public static partial class ParallelFactory + public static partial class AdvancedParallelFactory { /// /// Executes a parallel while loop. @@ -20,15 +16,14 @@ public static partial class ParallelFactory /// The function delegate that is responsible for the while loop condition. /// The function delegate that provides data from the specified . /// The delegate that will perform work while evaluates true. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Func worker, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default); - return WhileCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskActionFactory.Create(worker, default), setup); } /// @@ -42,15 +37,14 @@ public static Task WhileAsync(TReader reader, Func /// The function delegate that provides data from the specified . /// The delegate that will perform work while evaluates true. /// The parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T arg, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Func worker, T arg, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg); - return WhileCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskActionFactory.Create(worker, default, arg), setup); } /// @@ -66,15 +60,14 @@ public static Task WhileAsync(TReader reader, FuncThe delegate that will perform work while evaluates true. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2); - return WhileCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskActionFactory.Create(worker, default, arg1, arg2), setup); } /// @@ -92,15 +85,14 @@ public static Task WhileAsync(TReader reader, FuncThe first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3); - return WhileCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskActionFactory.Create(worker, default, arg1, arg2, arg3), setup); } /// @@ -120,15 +112,14 @@ public static Task WhileAsync(TReader reader, Fun /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4); - return WhileCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskActionFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); } /// @@ -150,52 +141,37 @@ public static Task WhileAsync(TReader reader, /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task WhileAsync(TReader reader, Func> condition, Func provider, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task WhileAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5); - return WhileCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskActionFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); } - private static async Task WhileCoreAsync(ForwardIterator iterator, ActionFactory workerFactory, Action setup) + private static async Task WhileCoreAsync(AsyncForwardIterator iterator, TaskActionFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); - var exceptions = new ConcurrentBag(); + var readForward = true; - while (true) { var workChunks = options.PartitionSize; var queue = new List(); - while (workChunks > 1 && readForward) + while (workChunks > 0 && readForward) { readForward = await iterator.ReadAsync().ConfigureAwait(false); if (!readForward) { break; } - var shallowWorkerFactory = workerFactory.Clone(); - queue.Add(Task.Factory.StartNew(element => - { - try - { - Interlocked.Decrement(ref workChunks); - shallowWorkerFactory.GenericArguments.Arg1 = (TElement)element; - shallowWorkerFactory.ExecuteMethod(); - } - catch (Exception e) - { - exceptions.Add(e); - } - }, iterator.Current, options.CancellationToken, options.CreationOptions, options.Scheduler)); + workerFactory.GenericArguments.Arg1 = iterator.Current; + queue.Add(workerFactory.ExecuteMethodAsync(options.CancellationToken)); + workChunks--; } if (queue.Count == 0) { break; } await Task.WhenAll(queue).ConfigureAwait(false); - if (workChunks > 1) { break; } } - if (exceptions.Count > 0) { throw new AggregateException(exceptions); } } } } \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.WhileResult.cs b/src/Cuemon.Threading/AdvancedParallelFactory.WhileResult.cs new file mode 100644 index 000000000..a5f4293a4 --- /dev/null +++ b/src/Cuemon.Threading/AdvancedParallelFactory.WhileResult.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; + +namespace Cuemon.Threading +{ + public static partial class AdvancedParallelFactory + { + /// + /// Executes a parallel while loop where the return value of the function delegate is stored in the same order as the while loop evaluates true. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The type of the return value of the function delegate . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same order as the while loop evaluates true. + public static IReadOnlyCollection WhileResult(TReader reader, Func condition, Func provider, Func worker, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + return WhileResultCore(new ForwardIterator(reader, condition, provider), FuncFactory.Create(worker, default), setup); + } + + /// + /// Executes a parallel while loop where the return value of the function delegate is stored in the same order as the while loop evaluates true. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The type of the parameter of the delegate . + /// The type of the return value of the function delegate . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The parameter of the delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same order as the while loop evaluates true. + public static IReadOnlyCollection WhileResult(TReader reader, Func condition, Func provider, Func worker, T arg, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + return WhileResultCore(new ForwardIterator(reader, condition, provider), FuncFactory.Create(worker, default, arg), setup); + } + + /// + /// Executes a parallel while loop where the return value of the function delegate is stored in the same order as the while loop evaluates true. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the return value of the function delegate . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same order as the while loop evaluates true. + public static IReadOnlyCollection WhileResult(TReader reader, Func condition, Func provider, Func worker, T1 arg1, T2 arg2, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + return WhileResultCore(new ForwardIterator(reader, condition, provider), FuncFactory.Create(worker, default, arg1, arg2), setup); + } + + /// + /// Executes a parallel while loop where the return value of the function delegate is stored in the same order as the while loop evaluates true. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the return value of the function delegate . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same order as the while loop evaluates true. + public static IReadOnlyCollection WhileResult(TReader reader, Func condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + return WhileResultCore(new ForwardIterator(reader, condition, provider), FuncFactory.Create(worker, default, arg1, arg2, arg3), setup); + } + + /// + /// Executes a parallel while loop where the return value of the function delegate is stored in the same order as the while loop evaluates true. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the return value of the function delegate . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same order as the while loop evaluates true. + public static IReadOnlyCollection WhileResult(TReader reader, Func condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + return WhileResultCore(new ForwardIterator(reader, condition, provider), FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); + } + + /// + /// Executes a parallel while loop where the return value of the function delegate is stored in the same order as the while loop evaluates true. + /// + /// The type of the that provides forward-only access to data. + /// The type of the result provided by . + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The type of the return value of the function delegate . + /// The reader that provides forward-only access to data. + /// The function delegate that is responsible for the while loop condition. + /// The function delegate that provides data from the specified . + /// The delegate that will perform work while evaluates true. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The fifth parameter of the delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same order as the while loop evaluates true. + public static IReadOnlyCollection WhileResult(TReader reader, Func condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + { + Validator.ThrowIfNull(condition, nameof(condition)); + Validator.ThrowIfNull(provider, nameof(provider)); + Validator.ThrowIfNull(worker, nameof(worker)); + return WhileResultCore(new ForwardIterator(reader, condition, provider), FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); + } + + private static IReadOnlyCollection WhileResultCore(ForwardIterator iterator, FuncFactory workerFactory, Action setup) + where TWorker : Template + { + return new FuncWhileSynchronousLoop(iterator, setup).GetResult(workerFactory); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.WhileResultAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs similarity index 79% rename from src/Cuemon.Threading/ParallelFactory.WhileResultAsync.cs rename to src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs index 0fdc25ea0..fdf2e91c1 100644 --- a/src/Cuemon.Threading/ParallelFactory.WhileResultAsync.cs +++ b/src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs @@ -8,7 +8,7 @@ namespace Cuemon.Threading { - public static partial class ParallelFactory + public static partial class AdvancedParallelFactory { /// /// Executes a parallel while loop where the return value of the function delegate is stored in the same order as the while loop evaluates true. @@ -20,16 +20,15 @@ public static partial class ParallelFactory /// The function delegate that is responsible for the while loop condition. /// The function delegate that provides data from the specified . /// The delegate that will perform work while evaluates true. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func> worker, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default); - return WhileResultCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileResultCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskFuncFactory.Create(worker, default), setup); } /// @@ -44,16 +43,15 @@ public static Task> WhileResultAsyncThe function delegate that provides data from the specified . /// The delegate that will perform work while evaluates true. /// The parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T arg, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func> worker, T arg, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg); - return WhileResultCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileResultCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskFuncFactory.Create(worker, default, arg), setup); } /// @@ -70,16 +68,15 @@ public static Task> WhileResultAsyncThe delegate that will perform work while evaluates true. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func> worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2); - return WhileResultCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileResultCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskFuncFactory.Create(worker, default, arg1, arg2), setup); } /// @@ -98,16 +95,15 @@ public static Task> WhileResultAsyncThe first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func> worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3); - return WhileResultCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileResultCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskFuncFactory.Create(worker, default, arg1, arg2, arg3), setup); } /// @@ -128,16 +124,15 @@ public static Task> WhileResultAsyncThe second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func> worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4); - return WhileResultCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileResultCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskFuncFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); } /// @@ -160,61 +155,44 @@ public static Task> WhileResultAsyncThe third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same order as the while loop evaluates true. - public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task> WhileResultAsync(TReader reader, Func> condition, Func provider, Func> worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(condition, nameof(condition)); Validator.ThrowIfNull(provider, nameof(provider)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5); - return WhileResultCoreAsync(new ForwardIterator(reader, condition, provider), wf, setup); + return WhileResultCoreAsync(new AsyncForwardIterator(reader, condition, provider), TaskFuncFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); } - private static async Task> WhileResultCoreAsync(ForwardIterator iterator, FuncFactory workerFactory, Action setup) + private static async Task> WhileResultCoreAsync(AsyncForwardIterator iterator, TaskFuncFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); - var exceptions = new ConcurrentBag(); var result = new ConcurrentDictionary(); var readForward = true; - var sorter = long.MinValue; + long sorter = 0; while (true) { var workChunks = options.PartitionSize; - var queue = new List(); - while (workChunks > 1 && readForward) + var queue = new Dictionary>(); + while (workChunks > 0 && readForward) { readForward = await iterator.ReadAsync().ConfigureAwait(false); if (!readForward) { break; } - var shallowWorkerFactory = workerFactory.Clone(); - var current = sorter; - queue.Add(Task.Factory.StartNew(element => - { - try - { - Interlocked.Decrement(ref workChunks); - shallowWorkerFactory.GenericArguments.Arg1 = (TElement)element; - var presult = shallowWorkerFactory.ExecuteMethod(); - result.TryAdd(current, presult); - } - catch (Exception e) - { - exceptions.Add(e); - } - }, iterator.Current, options.CancellationToken, options.CreationOptions, options.Scheduler)); - + workerFactory.GenericArguments.Arg1 = iterator.Current; + queue.Add(sorter, workerFactory.ExecuteMethodAsync(options.CancellationToken)); + workChunks--; sorter++; } if (queue.Count == 0) { break; } - await Task.WhenAll(queue).ConfigureAwait(false); - if (workChunks > 1) { break; } + await Task.WhenAll(queue.Values).ConfigureAwait(false); + foreach (var item in queue) { result.TryAdd(item.Key, item.Value.Result); } } - if (exceptions.Count > 0) { throw new AggregateException(exceptions); } return new ReadOnlyCollection(result.Values.ToList()); } } -} +} \ No newline at end of file diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.cs b/src/Cuemon.Threading/AdvancedParallelFactory.cs new file mode 100644 index 000000000..bc804c8bf --- /dev/null +++ b/src/Cuemon.Threading/AdvancedParallelFactory.cs @@ -0,0 +1,55 @@ +using System; + +namespace Cuemon.Threading +{ + /// + /// Provides a factory based way to work with advanced scenarios that encapsulate and re-use existing code while adding support for typically long-running parallel loops and regions. + /// + /// + public static partial class AdvancedParallelFactory + { + /// + /// Provides a default implementation of a for-iterator callback method. + /// + /// The type of the counter in a for-loop. + /// The current value of the counter in a for-loop. + /// One of the enumeration values that specifies the rules to apply as the assignment operator for left-hand operand and right-hand operand . + /// The value to assign to according to the rule specified by . + /// The computed result of having the of . + public static T Iterator(T current, AssignmentOperator assignment, T step) where T : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + return Calculator.Calculate(current, assignment, step); + } + + /// + /// Provides a default implementation of a for-condition callback method. + /// + /// The type of the counter in a for-loop. + /// The current value of the counter in a for-loop. + /// One of the enumeration values that specifies the rules to apply as the relational operator for left-hand operand and right-hand operand . + /// The amount of repeats to do according to the rules specified by . + /// true if does not meet the condition of and ; otherwise false. + public static bool Condition(T current, RelationalOperator relational, T repeats) where T : struct, IComparable, IEquatable, IConvertible + { + Calculator.ValidAsNumericOperand(); + switch (relational) + { + case RelationalOperator.Equal: + return current.Equals(repeats); + case RelationalOperator.GreaterThan: + return current.CompareTo(repeats) > 0; + case RelationalOperator.GreaterThanOrEqual: + return current.CompareTo(repeats) >= 0; + case RelationalOperator.LessThan: + return current.CompareTo(repeats) < 0; + case RelationalOperator.LessThanOrEqual: + return current.CompareTo(repeats) <= 0; + case RelationalOperator.NotEqual: + return !current.Equals(repeats); + default: + throw new ArgumentOutOfRangeException(nameof(relational)); + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/AsyncForwardIterator.cs b/src/Cuemon.Threading/AsyncForwardIterator.cs new file mode 100644 index 000000000..56ecca68a --- /dev/null +++ b/src/Cuemon.Threading/AsyncForwardIterator.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + internal class AsyncForwardIterator + { + internal AsyncForwardIterator(TReader reader, Func> condition, Func provider) + { + Reader = reader; + ConditionAsync = condition; + Provider = provider; + } + + private TReader Reader { get; } + + private Func> ConditionAsync { get; } + + private Func Provider { get; } + + public TElement Current { get; private set; } + + public async Task ReadAsync() + { + if (await ConditionAsync().ConfigureAwait(false)) + { + Current = Provider(Reader); + return true; + } + Current = default; + return false; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/TaskFactoryOptions.cs b/src/Cuemon.Threading/AsyncTaskFactoryOptions.cs similarity index 72% rename from src/Cuemon.Threading/TaskFactoryOptions.cs rename to src/Cuemon.Threading/AsyncTaskFactoryOptions.cs index cf0cbc0cf..5c42402f8 100644 --- a/src/Cuemon.Threading/TaskFactoryOptions.cs +++ b/src/Cuemon.Threading/AsyncTaskFactoryOptions.cs @@ -4,22 +4,22 @@ namespace Cuemon.Threading { /// - /// Configuration options for . + /// Configuration options for . /// - public class TaskFactoryOptions : AsyncOptions + public class AsyncTaskFactoryOptions : AsyncWorkloadOptions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// - /// The following table shows the initial property values for an instance of . + /// The following table shows the initial property values for an instance of . /// /// /// Property /// Initial Value /// /// - /// + /// /// 2 x /// /// @@ -32,19 +32,12 @@ public class TaskFactoryOptions : AsyncOptions /// /// /// - public TaskFactoryOptions() + public AsyncTaskFactoryOptions() { CreationOptions = TaskCreationOptions.LongRunning; Scheduler = TaskScheduler.Current; - PartitionSize = 2 * Environment.ProcessorCount; } - /// - /// Gets or sets the size of the partition to allocate work to a set of tasks. - /// - /// The size of the partition to allocate work to a set of tasks. - public int PartitionSize { get; set; } - /// /// Gets or sets the used to create the task. /// diff --git a/src/Cuemon.Threading/AsyncWorkloadOptions.cs b/src/Cuemon.Threading/AsyncWorkloadOptions.cs new file mode 100644 index 000000000..43781d81a --- /dev/null +++ b/src/Cuemon.Threading/AsyncWorkloadOptions.cs @@ -0,0 +1,37 @@ +using System; + +namespace Cuemon.Threading +{ + /// + /// Configuration options for . + /// + public class AsyncWorkloadOptions : AsyncOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// 2 x + /// + /// + /// + public AsyncWorkloadOptions() + { + PartitionSize = 2 * Environment.ProcessorCount; + } + + /// + /// Gets or sets the size of the partition to allocate work to a set of tasks. + /// + /// The size of the partition to allocate work to a set of tasks. + public int PartitionSize { get; set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/AsynchronousLoop.cs b/src/Cuemon.Threading/AsynchronousLoop.cs new file mode 100644 index 000000000..ab510aba7 --- /dev/null +++ b/src/Cuemon.Threading/AsynchronousLoop.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + internal abstract class AsynchronousLoop : Loop + { + protected AsynchronousLoop(Action setup) : base(setup) + { + } + + protected Task WhileExecuting() + { + return null; + } + + protected Task Process(IList queue) + { + return queue.Count == 0 ? Task.CompletedTask : Task.WhenAll(queue); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ForEachSynchronousLoop.cs b/src/Cuemon.Threading/ForEachSynchronousLoop.cs new file mode 100644 index 000000000..695df0245 --- /dev/null +++ b/src/Cuemon.Threading/ForEachSynchronousLoop.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Cuemon.Collections.Generic; + +namespace Cuemon.Threading +{ + internal abstract class ForEachSynchronousLoop : SynchronousLoop + { + protected ForEachSynchronousLoop(IEnumerable source, Action setup) : base(setup) + { + Partitioner = new PartitionerEnumerable(source, Options.PartitionSize); + WhileCondition = () => Partitioner.HasPartitions; + Sorter = 0; + } + + private PartitionerEnumerable Partitioner { get; set; } + + private long Sorter { get; set; } + + protected sealed override void FillWorkQueue(TemplateFactory worker, IList> queue) + { + foreach (var item in Partitioner) + { + var shallowWorkerFactory = worker.Clone(); + shallowWorkerFactory.GenericArguments.Arg1 = item; + var current = Sorter; + queue.Add(() => Task.Factory.StartNew(swf => + { + try + { + FillWorkQueueWorkerFactory(swf as TemplateFactory, current); + } + catch (Exception e) + { + Exceptions.Add(e); + } + }, shallowWorkerFactory, Options.CancellationToken, Options.CreationOptions, Options.Scheduler)); + Sorter++; + } + } + + protected abstract void FillWorkQueueWorkerFactory(TemplateFactory worker, long sorter) where TWorker : Template; + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ForLoopRuleset.cs b/src/Cuemon.Threading/ForLoopRuleset.cs new file mode 100644 index 000000000..e4f40e180 --- /dev/null +++ b/src/Cuemon.Threading/ForLoopRuleset.cs @@ -0,0 +1,92 @@ +using System; + +namespace Cuemon.Threading +{ + + /// + /// Specifies the rules of a for-loop control flow statement. + /// + /// The type of the number used with the loop control variable. + public class ForLoopRuleset where TOperand : struct, IComparable, IEquatable, IConvertible + { + /// + /// Initializes a new instance of the class. + /// + /// + /// is outside the range of allowed types.
+ /// Allowed types are: , , , , , , , , , or . + ///
+ public ForLoopRuleset() + { + Calculator.ValidAsNumericOperand(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The rules of a for-loop control flow statement. + /// The conditional value of the loop control variable. + /// The value to assign the loop control variable. + /// The relation between the loop control variable and . + /// The assignment statement of the loop control variable using . + /// The function delegate that represents the condition section of the for loop. Default value is . + /// The function delegate that represents the iterator section of the for loop. Default value is . + /// + /// is outside the range of allowed types.
+ /// Allowed types are: , , , , , , , , , or . + ///
+ public ForLoopRuleset(TOperand from, TOperand to, TOperand step, RelationalOperator relation = RelationalOperator.LessThan, AssignmentOperator assignment = AssignmentOperator.Addition, Func condition = null, Func iterator = null) + { + Calculator.ValidAsNumericOperand(); + From = from; + To = to; + Step = step; + Relation = relation; + Assignment = assignment; + Condition = condition ?? AdvancedParallelFactory.Condition; + Iterator = iterator ?? AdvancedParallelFactory.Iterator; + } + + /// + /// Gets or sets the initial value of the loop control variable. + /// + /// The initial value of the loop control variable. + public TOperand From { get; set; } + + /// + /// Gets or sets the relation between the loop control variable and . + /// + /// The relation between the loop control variable. + public RelationalOperator Relation { get; set; } + + /// + /// Gets or sets the conditional value of the loop control variable. + /// + /// The conditional value of the loop control variable. + public TOperand To { get; set; } + + /// + /// Gets or sets the assignment statement of the loop control variable using . + /// + /// The assignment statement of the loop control variable. + public AssignmentOperator Assignment { get; set; } + + /// + /// Gets or sets the number to assign the loop control variable. + /// + /// The number to assign the loop control variable. + public TOperand Step { get; set; } + + /// + /// Gets or sets the function delegate of a for-condition. + /// + /// The function delegate of a for-condition. + public Func Condition { get; set; } + + /// + /// Gets or sets the function delegate of a for-iterator. + /// + /// The function delegate of a for-iterator. + public Func Iterator { get; set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ForSynchronousLoop.cs b/src/Cuemon.Threading/ForSynchronousLoop.cs new file mode 100644 index 000000000..001706a52 --- /dev/null +++ b/src/Cuemon.Threading/ForSynchronousLoop.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + internal abstract class ForSynchronousLoop : SynchronousLoop where TSource : struct, IComparable, IEquatable, IConvertible + { + protected ForSynchronousLoop(ForLoopRuleset rules, Action setup) : base(setup) + { + Rules = rules; + From = rules.From; + WhileCondition = () => true; + } + + protected TSource From { get; set; } + + protected ForLoopRuleset Rules { get; } + + protected TSource Processed { get; set; } + + protected int WorkChunks { get; set; } + + protected sealed override void FillWorkQueue(TemplateFactory worker, IList> queue) + { + for (var i = From; Rules.Condition(i, Rules.Relation, Rules.To); i = Rules.Iterator(i, Rules.Assignment, Rules.Step)) + { + var shallowWorkerFactory = worker.Clone(); + shallowWorkerFactory.GenericArguments.Arg1 = i; + queue.Add(() => Task.Factory.StartNew(swf => + { + try + { + FillWorkQueueWorkerFactory(swf as TemplateFactory); + } + catch (Exception e) + { + Exceptions.Add(e); + } + }, shallowWorkerFactory, Options.CancellationToken, Options.CreationOptions, Options.Scheduler)); + + Processed = i; + WorkChunks--; + + if (WorkChunks == 0) { break; } + } + From = Calculator.Calculate(Processed, Rules.Assignment, Rules.Step); + } + + protected abstract void FillWorkQueueWorkerFactory(TemplateFactory worker) where TWorker : Template; + + protected sealed override void OnWhileExecutingBeforeFillWorkQueue() + { + WorkChunks = Options.PartitionSize; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ForwardIterator.cs b/src/Cuemon.Threading/ForwardIterator.cs index cc2899738..9539ee375 100644 --- a/src/Cuemon.Threading/ForwardIterator.cs +++ b/src/Cuemon.Threading/ForwardIterator.cs @@ -1,28 +1,27 @@ using System; -using System.Threading.Tasks; namespace Cuemon.Threading { internal class ForwardIterator { - internal ForwardIterator(TReader reader, Func> condition, Func provider) + internal ForwardIterator(TReader reader, Func condition, Func provider) { Reader = reader; - ConditionAsync = condition; + Condition = condition; Provider = provider; } private TReader Reader { get; } - private Func> ConditionAsync { get; } + private Func Condition { get; } private Func Provider { get; } public TElement Current { get; private set; } - public async Task ReadAsync() + public bool Read() { - if (await ConditionAsync().ConfigureAwait(false)) + if (Condition()) { Current = Provider(Reader); return true; diff --git a/src/Cuemon.Threading/FuncForEachSynchronousLoop.cs b/src/Cuemon.Threading/FuncForEachSynchronousLoop.cs new file mode 100644 index 000000000..f8e7fba97 --- /dev/null +++ b/src/Cuemon.Threading/FuncForEachSynchronousLoop.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace Cuemon.Threading +{ + internal sealed class FuncForEachSynchronousLoop : ForEachSynchronousLoop + { + public FuncForEachSynchronousLoop(IEnumerable source, Action setup) : base(source, setup) + { + } + + protected override void FillWorkQueueWorkerFactory(TemplateFactory worker, long sorter) + { + if (worker is FuncFactory wf) + { + var presult = wf.ExecuteMethod(); + Result.TryAdd(sorter, presult); + } + } + + private ConcurrentDictionary Result { get; } = new ConcurrentDictionary(); + + public IReadOnlyCollection GetResult(TemplateFactory worker) where TWorker : Template + { + PrepareExecution(worker); + return new ReadOnlyCollection(Result.Values.ToList()); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/FuncForSynchronousLoop.cs b/src/Cuemon.Threading/FuncForSynchronousLoop.cs new file mode 100644 index 000000000..28587e56b --- /dev/null +++ b/src/Cuemon.Threading/FuncForSynchronousLoop.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace Cuemon.Threading +{ + internal sealed class FuncForSynchronousLoop : ForSynchronousLoop where TSource : struct, IComparable, IEquatable, IConvertible + { + public FuncForSynchronousLoop(ForLoopRuleset rules, Action setup) : base(rules, setup) + { + } + + private ConcurrentDictionary Result { get; } = new ConcurrentDictionary(); + + + protected override void FillWorkQueueWorkerFactory(TemplateFactory worker) + { + if (worker is FuncFactory wf) + { + var presult = wf.ExecuteMethod(); + Result.TryAdd(wf.GenericArguments.Arg1, presult); + } + } + + public IReadOnlyCollection GetResult(TemplateFactory worker) where TWorker : Template + { + PrepareExecution(worker); + return new ReadOnlyCollection(Result.Values.ToList()); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/FuncWhileSynchronousLoop.cs b/src/Cuemon.Threading/FuncWhileSynchronousLoop.cs new file mode 100644 index 000000000..8f9b2b29a --- /dev/null +++ b/src/Cuemon.Threading/FuncWhileSynchronousLoop.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace Cuemon.Threading +{ + internal sealed class FuncWhileSynchronousLoop : WhileSynchronousLoop + { + public FuncWhileSynchronousLoop(ForwardIterator iterator, Action setup) : base(iterator, setup) + { + } + + private ConcurrentDictionary Result { get; } = new ConcurrentDictionary(); + + protected override void FillWorkQueueWorkerFactory(TemplateFactory worker, long sorter) + { + if (worker is FuncFactory wf) + { + var presult = wf.ExecuteMethod(); + Result.TryAdd(sorter, presult); + } + } + + public IReadOnlyCollection GetResult(TemplateFactory worker) where TWorker : Template + { + PrepareExecution(worker); + return new ReadOnlyCollection(Result.Values.ToList()); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/Loop.cs b/src/Cuemon.Threading/Loop.cs new file mode 100644 index 000000000..3d1d00f2f --- /dev/null +++ b/src/Cuemon.Threading/Loop.cs @@ -0,0 +1,14 @@ +using System; + +namespace Cuemon.Threading +{ + internal abstract class Loop where TOptions : AsyncOptions, new() + { + protected Loop(Action setup) + { + Options = Patterns.Configure(setup); + } + + protected TOptions Options { get; } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.For.cs b/src/Cuemon.Threading/ParallelFactory.For.cs new file mode 100644 index 000000000..5016a51eb --- /dev/null +++ b/src/Cuemon.Threading/ParallelFactory.For.cs @@ -0,0 +1,223 @@ +using System; + +namespace Cuemon.Threading +{ + public static partial class ParallelFactory + { + /// + /// Executes a parallel for loop. + /// + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The which may be configured. + public static void For(int fromInclusive, int toExclusive, Action worker, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The parameter of the delegate . + /// The which may be configured. + public static void For(int fromInclusive, int toExclusive, Action worker, T arg, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg, setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The which may be configured. + public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The which may be configured. + public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The which may be configured. + public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The fifth parameter of the delegate . + /// The which may be configured. + public static void For(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, arg5, setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The which may be configured. + public static void For(long fromInclusive, long toExclusive, Action worker, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The parameter of the delegate . + /// The which may be configured. + public static void For(long fromInclusive, long toExclusive, Action worker, T arg, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The which may be configured. + public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The which may be configured. + public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The which may be configured. + public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, setup: setup); + } + + /// + /// Executes a parallel for loop. + /// + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The fifth parameter of the delegate . + /// The which may be configured. + public static void For(long fromInclusive, long toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + AdvancedParallelFactory.For(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.ForAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForAsync.cs index aef84e397..1757d24ec 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForAsync.cs @@ -1,68 +1,23 @@ using System; -using System.Collections.Concurrent; -using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Cuemon.Threading { public static partial class ParallelFactory { - /// - /// Provides a default implementation of a for-iterator callback method. - /// - /// The type of the counter in a for-loop. - /// The current value of the counter in a for-loop. - /// One of the enumeration values that specifies the rules to apply as the assignment operator for left-hand operand and right-hand operand . - /// The value to assign to according to the rule specified by . - /// The computed result of having the of . - public static T Iterator(T current, AssignmentOperator assignment, T step) where T : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - return Calculator.Calculate(current, assignment, step); - } - - /// - /// Provides a default implementation of a for-condition callback method. - /// - /// The type of the counter in a for-loop. - /// The current value of the counter in a for-loop. - /// One of the enumeration values that specifies the rules to apply as the relational operator for left-hand operand and right-hand operand . - /// The amount of repeats to do according to the rules specified by . - /// true if does not meet the condition of and ; otherwise false. - public static bool Condition(T current, RelationalOperator relational, T repeats) where T : struct, IComparable, IEquatable, IConvertible - { - Calculator.ValidAsNumericOperand(); - switch (relational) - { - case RelationalOperator.Equal: - return current.Equals(repeats); - case RelationalOperator.GreaterThan: - return current.CompareTo(repeats) > 0; - case RelationalOperator.GreaterThanOrEqual: - return current.CompareTo(repeats) >= 0; - case RelationalOperator.LessThan: - return current.CompareTo(repeats) < 0; - case RelationalOperator.LessThanOrEqual: - return current.CompareTo(repeats) <= 0; - case RelationalOperator.NotEqual: - return !current.Equals(repeats); - default: - throw new ArgumentOutOfRangeException(nameof(relational)); - } - } - /// /// Executes a parallel for loop. /// /// The start index, inclusive. /// The end index, exclusive. /// The delegate that is invoked once per iteration. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, setup); } /// @@ -73,12 +28,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, Action work /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T arg, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T arg, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg, setup); } /// @@ -91,12 +46,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, ActionThe delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, setup); } /// @@ -111,12 +66,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, ActionThe first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, setup); } /// @@ -133,12 +88,12 @@ public static Task ForAsync(int fromInclusive, int toExclusive, Acti /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, setup); } /// @@ -157,225 +112,126 @@ public static Task ForAsync(int fromInclusive, int toExclusive, /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(int fromInclusive, int toExclusive, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task ForAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, arg5, setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. /// The type of the parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T arg, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T arg, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . /// The type of the third parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . /// The type of the third parameter of the delegate . /// The type of the fourth parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections. + /// Executes a parallel for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the delegate . /// The type of the second parameter of the delegate . /// The type of the third parameter of the delegate . /// The type of the fourth parameter of the delegate . /// The type of the fifth parameter of the delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task ForAsync(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5); - return ForCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); - } - - private static async Task ForCoreAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, ActionFactory workerFactory, Func condition, Func iterator, Action setup) - where TWorker : Template - where TNumber : struct, IComparable, IEquatable, IConvertible - { - if (condition == null) { condition = Condition; } - if (iterator == null) { iterator = Iterator; } - - var options = Patterns.Configure(setup); - var exceptions = new ConcurrentBag(); - - while (true) - { - var workChunks = options.PartitionSize; - var queue = new List(); - for (var i = @from; condition(i, relation, to); i = iterator(i, assignment, step)) - { - var shallowWorkerFactory = workerFactory.Clone(); - queue.Add(Task.Factory.StartNew(j => - { - try - { - shallowWorkerFactory.GenericArguments.Arg1 = (TNumber)j; - shallowWorkerFactory.ExecuteMethod(); - } - catch (Exception e) - { - exceptions.Add(e); - } - }, i, options.CancellationToken, options.CreationOptions, options.Scheduler)); - - workChunks--; - - if (workChunks == 0) - { - @from = Calculator.Calculate(i, assignment, step); - break; - } - } - if (queue.Count == 0) { break; } - await Task.WhenAll(queue).ConfigureAwait(false); - if (workChunks > 1) { break; } - } - if (exceptions.Count > 0) { throw new AggregateException(exceptions); } + return AdvancedParallelFactory.ForAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, arg5, setup: setup); } } } \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.ForEach.cs b/src/Cuemon.Threading/ParallelFactory.ForEach.cs new file mode 100644 index 000000000..1b0bf8399 --- /dev/null +++ b/src/Cuemon.Threading/ParallelFactory.ForEach.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; + +namespace Cuemon.Threading +{ + public static partial class ParallelFactory + { + /// + /// Executes a parallel foreach loop. + /// + /// The type of the data in the source. + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The which may be configured. + public static void ForEach(IEnumerable source, Action worker, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + ForEachCore(source, ActionFactory.Create(worker, default), setup); + } + + /// + /// Executes a parallel foreach loop. + /// + /// The type of the data in the source. + /// The type of the parameter of the delegate . + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The parameter of the delegate . + /// The which may be configured. + public static void ForEach(IEnumerable source, Action worker, T arg, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + ForEachCore(source, ActionFactory.Create(worker, default, arg), setup); + } + + /// + /// Executes a parallel foreach loop. + /// + /// The type of the data in the source. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The which may be configured. + public static void ForEach(IEnumerable source, Action worker, T1 arg1, T2 arg2, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + ForEachCore(source, ActionFactory.Create(worker, default, arg1, arg2), setup); + } + + /// + /// Executes a parallel foreach loop. + /// + /// The type of the data in the source. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The which may be configured. + public static void ForEach(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + ForEachCore(source, ActionFactory.Create(worker, default, arg1, arg2, arg3), setup); + } + + /// + /// Executes a parallel foreach loop. + /// + /// The type of the data in the source. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The which may be configured. + public static void ForEach(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + ForEachCore(source, ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); + } + + /// + /// Executes a parallel foreach loop. + /// + /// The type of the data in the source. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The first parameter of the delegate . + /// The second parameter of the delegate . + /// The third parameter of the delegate . + /// The fourth parameter of the delegate . + /// The fifth parameter of the delegate . + /// The which may be configured. + public static void ForEach(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + ForEachCore(source, ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); + } + + private static void ForEachCore(IEnumerable source, ActionFactory workerFactory, Action setup) where TWorker : Template + { + new ActionForEachSynchronousLoop(source, setup).PrepareExecution(workerFactory); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs index 3e3775d3d..1ab4b66e4 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Cuemon.Collections.Generic; @@ -14,14 +14,13 @@ public static partial class ParallelFactory /// The type of the data in the source. /// The sequence to iterate over parallel. /// The delegate that is invoked once per iteration. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Func worker, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default); - return ForEachCoreAsync(source, wf, setup); + return ForEachCoreAsync(source, TaskActionFactory.Create(worker, default), setup); } /// @@ -32,14 +31,13 @@ public static Task ForEachAsync(IEnumerable source, ActionThe sequence to iterate over parallel. /// The delegate that is invoked once per iteration. /// The parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T arg, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Func worker, T arg, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg); - return ForEachCoreAsync(source, wf, setup); + return ForEachCoreAsync(source, TaskActionFactory.Create(worker, default, arg), setup); } /// @@ -52,14 +50,13 @@ public static Task ForEachAsync(IEnumerable source, Action< /// The delegate that is invoked once per iteration. /// The first parameter of the delegate . /// The second parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2); - return ForEachCoreAsync(source, wf, setup); + return ForEachCoreAsync(source, TaskActionFactory.Create(worker, default, arg1, arg2), setup); } /// @@ -74,14 +71,13 @@ public static Task ForEachAsync(IEnumerable source, Ac /// The first parameter of the delegate . /// The second parameter of the delegate . /// The third parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3); - return ForEachCoreAsync(source, wf, setup); + return ForEachCoreAsync(source, TaskActionFactory.Create(worker, default, arg1, arg2, arg3), setup); } /// @@ -98,14 +94,13 @@ public static Task ForEachAsync(IEnumerable source /// The second parameter of the delegate . /// The third parameter of the delegate . /// The fourth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4); - return ForEachCoreAsync(source, wf, setup); + return ForEachCoreAsync(source, TaskActionFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); } /// @@ -124,47 +119,30 @@ public static Task ForEachAsync(IEnumerable so /// The third parameter of the delegate . /// The fourth parameter of the delegate . /// The fifth parameter of the delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. - public static Task ForEachAsync(IEnumerable source, Action worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task ForEachAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = ActionFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5); - return ForEachCoreAsync(source, wf, setup); + return ForEachCoreAsync(source, TaskActionFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); } - private static async Task ForEachCoreAsync(IEnumerable source, ActionFactory workerFactory, Action setup) - where TWorker : Template + private static async Task ForEachCoreAsync(IEnumerable source, TaskActionFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); - var exceptions = new ConcurrentBag(); - var partitioner = new PartitionerEnumerable(source, options.PartitionSize); while (partitioner.HasPartitions) { var queue = new List(); foreach (var item in partitioner) { - var shallowWorkerFactory = workerFactory.Clone(); - queue.Add(Task.Factory.StartNew(element => - { - try - { - shallowWorkerFactory.GenericArguments.Arg1 = (TSource)element; - shallowWorkerFactory.ExecuteMethod(); - } - catch (Exception e) - { - exceptions.Add(e); - } - }, item, options.CancellationToken, options.CreationOptions, options.Scheduler)); + workerFactory.GenericArguments.Arg1 = item; + queue.Add(workerFactory.ExecuteMethodAsync(options.CancellationToken)); } if (queue.Count == 0) { break; } await Task.WhenAll(queue).ConfigureAwait(false); } - - if (exceptions.Count > 0) { throw new AggregateException(exceptions); } } } } \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.ForEachResult.cs b/src/Cuemon.Threading/ParallelFactory.ForEachResult.cs new file mode 100644 index 000000000..6f04ac46b --- /dev/null +++ b/src/Cuemon.Threading/ParallelFactory.ForEachResult.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + public static partial class ParallelFactory + { + /// + /// Executes a parallel foreach loop where the return value of the function delegate is stored in the same sequential order as . + /// + /// The type of the data in the source. + /// The type of the return value of the function delegate . + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . + public static IReadOnlyCollection ForEachResult(IEnumerable source, Func worker, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForEachResultCore(source, FuncFactory.Create(worker, default), setup); + } + + /// + /// Executes a parallel foreach loop where the return value of the function delegate is stored in the same sequential order as . + /// + /// The type of the data in the source. + /// The type of the parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as . + public static IReadOnlyCollection ForEachResult(IEnumerable source, Func worker, T arg, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForEachResultCore(source, FuncFactory.Create(worker, default, arg), setup); + } + + /// + /// Executes a parallel foreach loop where the return value of the function delegate is stored in the same sequential order as . + /// + /// The type of the data in the source. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as . + public static IReadOnlyCollection ForEachResult(IEnumerable source, Func worker, T1 arg1, T2 arg2, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForEachResultCore(source, FuncFactory.Create(worker, default, arg1, arg2), setup); + } + + /// + /// Executes a parallel foreach loop where the return value of the function delegate is stored in the same sequential order as . + /// + /// The type of the data in the source. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as . + public static IReadOnlyCollection ForEachResult(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForEachResultCore(source, FuncFactory.Create(worker, default, arg1, arg2, arg3), setup); + } + + /// + /// Executes a parallel foreach loop where the return value of the function delegate is stored in the same sequential order as . + /// + /// The type of the data in the source. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as . + public static IReadOnlyCollection ForEachResult(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForEachResultCore(source, FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); + } + + /// + /// Executes a parallel foreach loop + /// + /// The type of the data in the source. + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the fifth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The sequence to iterate over parallel. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The fifth parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as . + public static IReadOnlyCollection ForEachResult(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(worker, nameof(worker)); + return ForEachResultCore(source, FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); + } + + private static IReadOnlyCollection ForEachResultCore(IEnumerable source, FuncFactory workerFactory, Action setup) + where TWorker : Template + { + return new FuncForEachSynchronousLoop(source, setup).GetResult(workerFactory); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs index a0eec57d4..855020712 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForEachResultAsync.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Cuemon.Collections.Generic; @@ -17,15 +18,14 @@ public static partial class ParallelFactory /// The type of the return value of the function delegate . /// The sequence to iterate over parallel. /// The delegate that is invoked once per iteration. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func> worker, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default); - return ForEachResultCoreAsync(source, wf, setup); + return ForEachResultCoreAsync(source, TaskFuncFactory.Create(worker, default), setup); } /// @@ -37,15 +37,14 @@ public static Task> ForEachResultAsyncThe sequence to iterate over parallel. /// The delegate that is invoked once per iteration. /// The parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T arg, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func> worker, T arg, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg); - return ForEachResultCoreAsync(source, wf, setup); + return ForEachResultCoreAsync(source, TaskFuncFactory.Create(worker, default, arg), setup); } /// @@ -59,15 +58,14 @@ public static Task> ForEachResultAsyncThe delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func> worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2); - return ForEachResultCoreAsync(source, wf, setup); + return ForEachResultCoreAsync(source, TaskFuncFactory.Create(worker, default, arg1, arg2), setup); } /// @@ -83,15 +81,14 @@ public static Task> ForEachResultAsyncThe first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func> worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3); - return ForEachResultCoreAsync(source, wf, setup); + return ForEachResultCoreAsync(source, TaskFuncFactory.Create(worker, default, arg1, arg2, arg3), setup); } /// @@ -109,15 +106,14 @@ public static Task> ForEachResultAsyncThe second parameter of the function delegate . /// The third parameter of the function delegate . /// The fourth parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func> worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4); - return ForEachResultCoreAsync(source, wf, setup); + return ForEachResultCoreAsync(source, TaskFuncFactory.Create(worker, default, arg1, arg2, arg3, arg4), setup); } /// @@ -137,55 +133,37 @@ public static Task> ForEachResultAsyncThe third parameter of the function delegate . /// The fourth parameter of the function delegate . /// The fifth parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as . - public static Task> ForEachResultAsync(IEnumerable source, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + public static Task> ForEachResultAsync(IEnumerable source, Func> worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5); - return ForEachResultCoreAsync(source, wf, setup); + return ForEachResultCoreAsync(source, TaskFuncFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5), setup); } - private static async Task> ForEachResultCoreAsync(IEnumerable source, FuncFactory workerFactory, Action setup) + private static async Task> ForEachResultCoreAsync(IEnumerable source, TaskFuncFactory workerFactory, Action setup) where TWorker : Template { var options = Patterns.Configure(setup); - var exceptions = new ConcurrentBag(); var result = new ConcurrentDictionary(); - var sorter = 0; + long sorter = 0; var partitioner = new PartitionerEnumerable(source, options.PartitionSize); while (partitioner.HasPartitions) { - var queue = new List(); + var queue = new Dictionary>(); foreach (var item in partitioner) { - var shallowWorkerFactory = workerFactory.Clone(); - var current = sorter; - queue.Add(Task.Factory.StartNew(element => - { - try - { - shallowWorkerFactory.GenericArguments.Arg1 = (TSource)element; - var presult = shallowWorkerFactory.ExecuteMethod(); - result.TryAdd(current, presult); - } - catch (Exception e) - { - exceptions.Add(e); - } - }, item, options.CancellationToken, options.CreationOptions, options.Scheduler)); - + workerFactory.GenericArguments.Arg1 = item; + queue.Add(sorter, workerFactory.ExecuteMethodAsync(options.CancellationToken)); sorter++; - } if (queue.Count == 0) { break; } - await Task.WhenAll(queue).ConfigureAwait(false); + await Task.WhenAll(queue.Values).ConfigureAwait(false); + foreach (var item in queue) { result.TryAdd(item.Key, item.Value.Result); } } - - if (exceptions.Count > 0) { throw new AggregateException(exceptions); } return new ReadOnlyCollection(result.Values.ToList()); } } diff --git a/src/Cuemon.Threading/ParallelFactory.ForResult.cs b/src/Cuemon.Threading/ParallelFactory.ForResult.cs new file mode 100644 index 000000000..521a1ae41 --- /dev/null +++ b/src/Cuemon.Threading/ParallelFactory.ForResult.cs @@ -0,0 +1,248 @@ +using System; +using System.Collections.Generic; + +namespace Cuemon.Threading +{ + public static partial class ParallelFactory + { + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(int fromInclusive, int toExclusive, Func worker, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, setup); + } + + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(int fromInclusive, int toExclusive, Func worker, T arg, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg, setup); + } + + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, setup); + } + + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, setup); + } + + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, setup); + } + + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the fifth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The fifth parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, arg5, setup); + } + + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(long fromInclusive, long toExclusive, Func worker, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, setup: setup); + } + + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(long fromInclusive, long toExclusive, Func worker, T arg, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg, setup: setup); + } + + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, setup: setup); + } + + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, setup: setup); + } + + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, setup: setup); + } + + /// + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. + /// + /// The type of the first parameter of the function delegate . + /// The type of the second parameter of the function delegate . + /// The type of the third parameter of the function delegate . + /// The type of the fourth parameter of the function delegate . + /// The type of the fifth parameter of the function delegate . + /// The type of the return value of the function delegate . + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + /// The first parameter of the function delegate . + /// The second parameter of the function delegate . + /// The third parameter of the function delegate . + /// The fourth parameter of the function delegate . + /// The fifth parameter of the function delegate . + /// The which may be configured. + /// An where the return value of the function delegate is stored in the same sequential order as the for loop. + public static IReadOnlyCollection ForResult(long fromInclusive, long toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + { + Validator.ThrowIfNull(worker, nameof(worker)); + return AdvancedParallelFactory.ForResult(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs index 2afa3801d..892455e38 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForResultAsync.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; +using System.Threading; using System.Threading.Tasks; namespace Cuemon.Threading @@ -16,13 +14,13 @@ public static partial class ParallelFactory /// The start index, inclusive. /// The end index, exclusive. /// The delegate that is invoked once per iteration. - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func> worker, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, setup: setup); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, setup); } /// @@ -34,13 +32,13 @@ public static Task> ForResultAsync(int fro /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T arg, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func> worker, T arg, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg, setup: setup); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg, setup); } /// @@ -54,13 +52,13 @@ public static Task> ForResultAsync(int /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func> worker, T1 arg1, T2 arg2, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, setup: setup); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, setup); } /// @@ -76,13 +74,13 @@ public static Task> ForResultAsync /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func> worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, setup: setup); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, setup); } /// @@ -100,13 +98,13 @@ public static Task> ForResultAsyncThe second parameter of the function delegate . /// The third parameter of the function delegate . /// The fourth parameter of the function delegate . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func> worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, setup: setup); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, setup); } /// @@ -126,241 +124,139 @@ public static Task> ForResultAsyncThe third parameter of the function delegate . /// The fourth parameter of the function delegate . /// The fifth parameter of the function delegate . - /// The which may be configured. - /// A that represents the asynchronous operation. - public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) + /// The which may be configured. + /// A that represents the asynchronous operation. + /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. + public static Task> ForResultAsync(int fromInclusive, int toExclusive, Func> worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { Validator.ThrowIfNull(worker, nameof(worker)); - return ForResultAsync(fromInclusive, RelationalOperator.LessThan, toExclusive, AssignmentOperator.Addition, 1, worker, arg1, arg2, arg3, arg4, arg5, setup: setup); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, arg5, setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func> worker, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the parameter of the function delegate . /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T arg, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func> worker, T arg, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func> worker, T1 arg1, T2 arg2, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func> worker, T1 arg1, T2 arg2, T3 arg3, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the fourth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . /// The fourth parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func> worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, setup: setup); } /// - /// Executes a parallel for loop that offers control of the loop control variable and loop sections where the return value of the function delegate is stored in the same sequential order as the for loop. + /// Executes a parallel for loop where the return value of the function delegate is stored in the same sequential order as the for loop. /// - /// The type of the number used with the loop control variable. /// The type of the first parameter of the function delegate . /// The type of the second parameter of the function delegate . /// The type of the third parameter of the function delegate . /// The type of the fourth parameter of the function delegate . /// The type of the fifth parameter of the function delegate . /// The type of the return value of the function delegate . - /// The initial value of the loop control variable. - /// The relation between the loop control variable and . - /// The conditional value of the loop control variable. - /// The assignment statement of the loop control variable using . - /// The value to assign the loop control variable. + /// The start index, inclusive. + /// The end index, exclusive. /// The delegate that is invoked once per iteration. /// The first parameter of the function delegate . /// The second parameter of the function delegate . /// The third parameter of the function delegate . /// The fourth parameter of the function delegate . /// The fifth parameter of the function delegate . - /// The function delegate that represents the condition section of the for loop. Default value is . - /// The function delegate that represents the iterator section of the for loop. Default value is . - /// The which may be configured. + /// The which may be configured. /// A that represents the asynchronous operation. /// The task result contains an where the return value of the function delegate is stored in the same sequential order as the for loop. - public static Task> ForResultAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, Func worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func condition = null, Func iterator = null, Action setup = null) - where TNumber : struct, IComparable, IEquatable, IConvertible + public static Task> ForResultAsync(long fromInclusive, long toExclusive, Func> worker, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action setup = null) { - Calculator.ValidAsNumericOperand(); Validator.ThrowIfNull(worker, nameof(worker)); - var wf = FuncFactory.Create(worker, default, arg1, arg2, arg3, arg4, arg5); - return ForResultCoreAsync(@from, relation, to, assignment, step, wf, condition, iterator, setup); - } - - private static async Task> ForResultCoreAsync(TNumber from, RelationalOperator relation, TNumber to, AssignmentOperator assignment, TNumber step, FuncFactory workerFactory, Func condition, Func iterator, Action setup) - where TWorker : Template - where TNumber : struct, IComparable, IEquatable, IConvertible - { - if (condition == null) { condition = Condition; } - if (iterator == null) { iterator = Iterator; } - - var options = Patterns.Configure(setup); - var exceptions = new ConcurrentBag(); - var result = new ConcurrentDictionary(); - - while (true) - { - var workChunks = options.PartitionSize; - var queue = new List(); - for (var i = @from; condition(i, relation, to); i = iterator(i, assignment, step)) - { - var shallowWorkerFactory = workerFactory.Clone(); - queue.Add(Task.Factory.StartNew(j => - { - try - { - var number = (TNumber)j; - shallowWorkerFactory.GenericArguments.Arg1 = number; - var presult = shallowWorkerFactory.ExecuteMethod(); - result.TryAdd(number, presult); - } - catch (Exception e) - { - exceptions.Add(e); - } - }, i, options.CancellationToken, options.CreationOptions, options.Scheduler)); - - workChunks--; - - if (workChunks == 0) - { - @from = Calculator.Calculate(i, assignment, step); - break; - } - } - if (queue.Count == 0) { break; } - await Task.WhenAll(queue).ConfigureAwait(false); - if (workChunks > 1) { break; } - } - if (exceptions.Count > 0) { throw new AggregateException(exceptions); } - return new ReadOnlyCollection(result.Values.ToList()); + return AdvancedParallelFactory.ForResultAsync(new ForLoopRuleset(fromInclusive, toExclusive, 1), worker, arg1, arg2, arg3, arg4, arg5, setup: setup); } } } \ No newline at end of file diff --git a/src/Cuemon.Threading/ParallelFactory.cs b/src/Cuemon.Threading/ParallelFactory.cs new file mode 100644 index 000000000..e4ab515eb --- /dev/null +++ b/src/Cuemon.Threading/ParallelFactory.cs @@ -0,0 +1,9 @@ +namespace Cuemon.Threading +{ + /// + /// Provides a factory based way to encapsulate and re-use existing code while adding support for typically long-running parallel loops and regions. + /// + public static partial class ParallelFactory + { + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/SynchronousLoop.cs b/src/Cuemon.Threading/SynchronousLoop.cs new file mode 100644 index 000000000..d03245475 --- /dev/null +++ b/src/Cuemon.Threading/SynchronousLoop.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + internal abstract class SynchronousLoop : Loop + { + protected SynchronousLoop(Action setup) : base(setup) + { + } + + protected ConcurrentBag Exceptions { get; } = new ConcurrentBag(); + + protected Func WhileCondition { get; set; } + + public void PrepareExecution(TemplateFactory worker) where TWorker : Template + { + WhileExecuting(worker); + } + + protected void WhileExecuting(TemplateFactory worker) where TWorker : Template + { + if (WhileCondition == null) { throw new InvalidOperationException($"{nameof(WhileCondition)} cannot be null."); } + while (WhileCondition()) + { + OnWhileExecutingBeforeFillWorkQueue(); + var queue = new List>(); + FillWorkQueue(worker, queue); + if (queue.Count == 0) { break; } + Process(queue); + } + if (Exceptions.Count > 0) { throw new AggregateException(Exceptions); } + } + + protected virtual void OnWhileExecutingBeforeFillWorkQueue() + { + } + + protected abstract void FillWorkQueue(TemplateFactory worker, IList> queue) where TWorker : Template; + + protected void Process(IList> queue) + { + try + { + Task.WaitAll(queue.Select(func => func()).ToArray(), Options.CancellationToken); + } + catch (Exception e) + { + Exceptions.Add(e); + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Threading/WhileSynchronousLoop.cs b/src/Cuemon.Threading/WhileSynchronousLoop.cs new file mode 100644 index 000000000..55269da6a --- /dev/null +++ b/src/Cuemon.Threading/WhileSynchronousLoop.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Cuemon.Threading +{ + internal abstract class WhileSynchronousLoop : SynchronousLoop + { + private int _workChunks; + + protected WhileSynchronousLoop(ForwardIterator iterator, Action setup) : base(setup) + { + Iterator = iterator; + ReadForward = true; + WhileCondition = () => true; + Sorter = 0; + } + + private ForwardIterator Iterator { get; } + + private bool ReadForward { get; set; } + + private long Sorter { get; set; } + + protected override void OnWhileExecutingBeforeFillWorkQueue() + { + _workChunks = Options.PartitionSize; + } + + protected sealed override void FillWorkQueue(TemplateFactory worker, IList> queue) + { + while (_workChunks > 0 && ReadForward) + { + ReadForward = Iterator.Read(); + if (!ReadForward) { return; } + var shallowWorkerFactory = worker.Clone(); + shallowWorkerFactory.GenericArguments.Arg1 = Iterator.Current; + var current = Sorter; + queue.Add(() => Task.Factory.StartNew(swf => + { + try + { + Interlocked.Decrement(ref _workChunks); + FillWorkQueueWorkerFactory(swf as TemplateFactory, current); + } + catch (Exception e) + { + Exceptions.Add(e); + } + }, shallowWorkerFactory, Options.CancellationToken, Options.CreationOptions, Options.Scheduler)); + Sorter++; + } + } + + protected abstract void FillWorkQueueWorkerFactory(TemplateFactory worker, long sorter) where TWorker : Template; + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Net.Tests/Http/UriExtensionsTest.cs b/test/Cuemon.Extensions.Net.Tests/Http/UriExtensionsTest.cs index 7ad54736d..4f9d7a526 100644 --- a/test/Cuemon.Extensions.Net.Tests/Http/UriExtensionsTest.cs +++ b/test/Cuemon.Extensions.Net.Tests/Http/UriExtensionsTest.cs @@ -27,9 +27,9 @@ public async Task HttpGetAsync_ShouldGetResponseFromUri() var uri = new Uri("https://www.cuemon.net/"); var expected = 125; var atomicCount = 0; - await ParallelFactory.ForAsync(0, expected, i => + await ParallelFactory.ForAsync(0, expected, async (i, ct) => { - using (var response = uri.HttpGetAsync().GetAwaiter().GetResult()) + using (var response = await uri.HttpGetAsync(ct)) { Interlocked.Increment(ref atomicCount); Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs index 89597d1ca..24d984eb1 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Cuemon.Extensions.Xunit; +using Microsoft.VisualStudio.TestPlatform.Utilities; using Xunit; using Xunit.Abstractions; @@ -57,12 +58,17 @@ public void For_ShouldRunConcurrent_IgniteCancellation() Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); - Thread.Sleep(500); // wait for possible background threads being canceled - TestOutput.WriteLine(x.ToString()); TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + + var remaining = 1; + while (remaining > 0) // exhaust remaining threads + { + var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); + remaining = differenceBecauseOfBackgroundCancellation.Count; + } Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } @@ -142,12 +148,17 @@ public void ForResult_ShouldRunConcurrent_IgniteCancellation() Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); - Thread.Sleep(500); // wait for possible background threads being canceled - TestOutput.WriteLine(x.ToString()); TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + + var remaining = 1; + while (remaining > 0) // exhaust remaining threads + { + var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); + remaining = differenceBecauseOfBackgroundCancellation.Count; + } Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } @@ -227,12 +238,17 @@ public void ForEach_ShouldRunConcurrent_IgniteCancellation() Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); - Thread.Sleep(500); // wait for possible background threads being canceled - TestOutput.WriteLine(x.ToString()); TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + + var remaining = 1; + while (remaining > 0) // exhaust remaining threads + { + var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); + remaining = differenceBecauseOfBackgroundCancellation.Count; + } Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } @@ -320,6 +336,13 @@ public void ForEachResult_ShouldRunConcurrent_IgniteCancellation() TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + + var remaining = 1; + while (remaining > 0) // exhaust remaining threads + { + var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); + remaining = differenceBecauseOfBackgroundCancellation.Count; + } Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } @@ -403,12 +426,17 @@ public void While_ShouldRunConcurrent_IgniteCancellation() Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); - Thread.Sleep(500); // wait for possible background threads being canceled - TestOutput.WriteLine(x.ToString()); TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + + var remaining = 1; + while (remaining > 0) // exhaust remaining threads + { + var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); + remaining = differenceBecauseOfBackgroundCancellation.Count; + } Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } @@ -494,12 +522,17 @@ public void WhileResult_ShouldRunConcurrent_IgniteCancellation() Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); - Thread.Sleep(500); // wait for possible background threads being canceled - TestOutput.WriteLine(x.ToString()); TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation + + var remaining = 1; + while (remaining > 0) // exhaust remaining threads + { + var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); + remaining = differenceBecauseOfBackgroundCancellation.Count; + } Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } From 88a4ee4b810958c0b84f370ddcc8866112a391f6 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 02:20:11 +0200 Subject: [PATCH 165/385] Fix --- test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs index 895583968..daffbba2b 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs @@ -410,7 +410,7 @@ public async Task WhileResultAsync_ShouldRunConcurrent() await Task.Delay(50, ct); cb.Add(i); return i; - }, o => o.PartitionSize = 1); + }, o => o.PartitionSize = 64); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); From 94b65dda55579c158946b95e70b6ec9e5996d1e1 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 02:34:10 +0200 Subject: [PATCH 166/385] Ready for 6.0 --- .../ActionForSynchronousLoop.cs | 4 +- .../AdvancedParallelFactory.ForAsync.cs | 1 + .../AdvancedParallelFactory.ForResultAsync.cs | 2 +- .../AdvancedParallelFactory.WhileAsync.cs | 2 +- ...dvancedParallelFactory.WhileResultAsync.cs | 1 - src/Cuemon.Threading/AsynchronousLoop.cs | 23 --- src/Cuemon.Threading/ForSynchronousLoop.cs | 12 +- .../FuncForSynchronousLoop.cs | 8 +- src/Cuemon.Threading/GlobalSuppressions.cs | 160 ++++++++++++------ .../ParallelFactory.ForEachAsync.cs | 1 + 10 files changed, 121 insertions(+), 93 deletions(-) delete mode 100644 src/Cuemon.Threading/AsynchronousLoop.cs diff --git a/src/Cuemon.Threading/ActionForSynchronousLoop.cs b/src/Cuemon.Threading/ActionForSynchronousLoop.cs index 3bd0dc6e2..54fc8ce61 100644 --- a/src/Cuemon.Threading/ActionForSynchronousLoop.cs +++ b/src/Cuemon.Threading/ActionForSynchronousLoop.cs @@ -2,9 +2,9 @@ namespace Cuemon.Threading { - internal sealed class ActionForSynchronousLoop : ForSynchronousLoop where TSource : struct, IComparable, IEquatable, IConvertible + internal sealed class ActionForSynchronousLoop : ForSynchronousLoop where TOperand : struct, IComparable, IEquatable, IConvertible { - public ActionForSynchronousLoop(ForLoopRuleset rules, Action setup) : base(rules, setup) + public ActionForSynchronousLoop(ForLoopRuleset rules, Action setup) : base(rules, setup) { } diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs index 37be592e6..8bf24902d 100644 --- a/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs +++ b/src/Cuemon.Threading/AdvancedParallelFactory.ForAsync.cs @@ -164,6 +164,7 @@ private static async Task ForCoreAsync(ForLoopRuleset> ForResultCoreAsync(); - TOperand processed = default; + while (true) { var workChunks = options.PartitionSize; diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs index e09f31ca5..14c769ca6 100644 --- a/src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs +++ b/src/Cuemon.Threading/AdvancedParallelFactory.WhileAsync.cs @@ -155,8 +155,8 @@ private static async Task WhileCoreAsync(AsyncForwar where TWorker : Template { var options = Patterns.Configure(setup); - var readForward = true; + while (true) { var workChunks = options.PartitionSize; diff --git a/src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs b/src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs index fdf2e91c1..f2cfdfa94 100644 --- a/src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs +++ b/src/Cuemon.Threading/AdvancedParallelFactory.WhileResultAsync.cs @@ -182,7 +182,6 @@ private static async Task> WhileResultCoreAsync - { - protected AsynchronousLoop(Action setup) : base(setup) - { - } - - protected Task WhileExecuting() - { - return null; - } - - protected Task Process(IList queue) - { - return queue.Count == 0 ? Task.CompletedTask : Task.WhenAll(queue); - } - } -} \ No newline at end of file diff --git a/src/Cuemon.Threading/ForSynchronousLoop.cs b/src/Cuemon.Threading/ForSynchronousLoop.cs index 001706a52..9b1dce175 100644 --- a/src/Cuemon.Threading/ForSynchronousLoop.cs +++ b/src/Cuemon.Threading/ForSynchronousLoop.cs @@ -4,20 +4,20 @@ namespace Cuemon.Threading { - internal abstract class ForSynchronousLoop : SynchronousLoop where TSource : struct, IComparable, IEquatable, IConvertible + internal abstract class ForSynchronousLoop : SynchronousLoop where TOperand : struct, IComparable, IEquatable, IConvertible { - protected ForSynchronousLoop(ForLoopRuleset rules, Action setup) : base(setup) + protected ForSynchronousLoop(ForLoopRuleset rules, Action setup) : base(setup) { Rules = rules; From = rules.From; WhileCondition = () => true; } - protected TSource From { get; set; } + protected TOperand From { get; set; } - protected ForLoopRuleset Rules { get; } + protected ForLoopRuleset Rules { get; } - protected TSource Processed { get; set; } + protected TOperand Processed { get; set; } protected int WorkChunks { get; set; } @@ -47,7 +47,7 @@ protected sealed override void FillWorkQueue(TemplateFactory w From = Calculator.Calculate(Processed, Rules.Assignment, Rules.Step); } - protected abstract void FillWorkQueueWorkerFactory(TemplateFactory worker) where TWorker : Template; + protected abstract void FillWorkQueueWorkerFactory(TemplateFactory worker) where TWorker : Template; protected sealed override void OnWhileExecutingBeforeFillWorkQueue() { diff --git a/src/Cuemon.Threading/FuncForSynchronousLoop.cs b/src/Cuemon.Threading/FuncForSynchronousLoop.cs index 28587e56b..f3624575f 100644 --- a/src/Cuemon.Threading/FuncForSynchronousLoop.cs +++ b/src/Cuemon.Threading/FuncForSynchronousLoop.cs @@ -6,13 +6,13 @@ namespace Cuemon.Threading { - internal sealed class FuncForSynchronousLoop : ForSynchronousLoop where TSource : struct, IComparable, IEquatable, IConvertible + internal sealed class FuncForSynchronousLoop : ForSynchronousLoop where TOperand : struct, IComparable, IEquatable, IConvertible { - public FuncForSynchronousLoop(ForLoopRuleset rules, Action setup) : base(rules, setup) + public FuncForSynchronousLoop(ForLoopRuleset rules, Action setup) : base(rules, setup) { } - private ConcurrentDictionary Result { get; } = new ConcurrentDictionary(); + private ConcurrentDictionary Result { get; } = new ConcurrentDictionary(); protected override void FillWorkQueueWorkerFactory(TemplateFactory worker) @@ -24,7 +24,7 @@ protected override void FillWorkQueueWorkerFactory(TemplateFactory GetResult(TemplateFactory worker) where TWorker : Template + public IReadOnlyCollection GetResult(TemplateFactory worker) where TWorker : Template { PrepareExecution(worker); return new ReadOnlyCollection(Result.Values.ToList()); diff --git a/src/Cuemon.Threading/GlobalSuppressions.cs b/src/Cuemon.Threading/GlobalSuppressions.cs index 0908ff703..813a627fc 100644 --- a/src/Cuemon.Threading/GlobalSuppressions.cs +++ b/src/Cuemon.Threading/GlobalSuppressions.cs @@ -5,58 +5,108 @@ using System.Diagnostics.CodeAnalysis; -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``1(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0},System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``2(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1},``1,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``3(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2},``1,``2,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3},``1,``2,``3,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3},``1,``2,``3,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3},``0,``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3},``0,``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3,``4},``1,``2,``3,``4,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3,``4},``1,``2,``3,``4,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``6(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``6(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForCoreAsync``2(``1,Cuemon.RelationalOperator,``1,Cuemon.AssignmentOperator,``1,Cuemon.ActionFactory{``0},System.Func{``1,Cuemon.RelationalOperator,``1,System.Boolean},System.Func{``1,Cuemon.AssignmentOperator,``1,``1},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``4(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3},``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``5(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3,``4},``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``6(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``6(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``4(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3},``1,``2,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``5(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4},``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``6(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``7(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``7(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``2(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1},System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``1}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``3(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2},``1,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``2}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``4(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3},``1,``2,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``4(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3},``1,``2,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``4(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3},``0,``1,``2,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4},``1,``2,``3,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4},``1,``2,``3,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``7(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``7(``0,Cuemon.RelationalOperator,``0,Cuemon.AssignmentOperator,``0,System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Func{``0,Cuemon.RelationalOperator,``0,System.Boolean},System.Func{``0,Cuemon.AssignmentOperator,``0,``0},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultCoreAsync``3(``1,Cuemon.RelationalOperator,``1,Cuemon.AssignmentOperator,``1,Cuemon.FuncFactory{``0,``2},System.Func{``1,Cuemon.RelationalOperator,``1,System.Boolean},System.Func{``1,Cuemon.AssignmentOperator,``1,``1},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``2}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``4(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3},``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``5(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4},``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``5(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4},``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5},``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5},``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``4(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3},``2,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``5(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4},``2,``3,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5},``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5},``2,``3,``4,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``8(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6,``7},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``7}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultAsync``8(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6,``7},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``7}}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.WhileResultCoreAsync``4(Cuemon.Threading.ForwardIterator{``0,``1},Cuemon.FuncFactory{``2,``3},System.Action{Cuemon.Threading.TaskFactoryOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.For``4(Cuemon.Threading.ForLoopRuleset{``0},System.Action{``0,``1,``2,``3},``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.For``5(Cuemon.Threading.ForLoopRuleset{``0},System.Action{``0,``1,``2,``3,``4},``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.For``6(Cuemon.Threading.ForLoopRuleset{``0},System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.For``6(Cuemon.Threading.ForLoopRuleset{``0},System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForAsync``4(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task},``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForAsync``5(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForAsync``6(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForAsync``6(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForResult``4(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3},``1,``2,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForResult``5(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,``4},``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForResult``6(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForResult``7(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``6}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForResult``7(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForResultAsync``4(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``1,``2,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForResultAsync``5(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForResultAsync``6(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForResultAsync``7(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task{``6}},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.ForResultAsync``7(Cuemon.Threading.ForLoopRuleset{``0},System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task{``6}},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.While``4(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Action{``1,``2,``3},``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.While``5(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Action{``1,``2,``3,``4},``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.While``5(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Action{``1,``2,``3,``4},``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.While``6(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5},``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.While``6(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5},``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.While``7(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.While``7(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Action{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileAsync``4(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task},``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileAsync``5(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileAsync``5(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task},``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task},``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResult``4(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Func{``1,``2,``3},``2,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResult``5(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Func{``1,``2,``3,``4},``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResult``6(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5},``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResult``6(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5},``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResult``7(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``6}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResult``7(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6},``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``6}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResult``8(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6,``7},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``7}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResult``8(``0,System.Func{System.Boolean},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6,``7},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``7}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResultAsync``4(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``2,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResultAsync``5(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResultAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResultAsync``6(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResultAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task{``6}},``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResultAsync``7(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task{``6}},``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResultAsync``8(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task{``7}},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``7}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResultAsync``8(``0,System.Func{System.Threading.Tasks.Task{System.Boolean}},System.Func{``0,``1},System.Func{``1,``2,``3,``4,``5,``6,System.Threading.CancellationToken,System.Threading.Tasks.Task{``7}},``2,``3,``4,``5,``6,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``7}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResultCore``4(Cuemon.Threading.ForwardIterator{``0,``1},Cuemon.FuncFactory{``2,``3},System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.AdvancedParallelFactory.WhileResultCoreAsync``4(Cuemon.Threading.AsyncForwardIterator{``0,``1},Cuemon.TaskFuncFactory{``2,``3},System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.For``4(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.For``4(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.For``4(System.Int64,System.Int64,System.Action{System.Int64,``0,``1,``2,``3},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.For``4(System.Int64,System.Int64,System.Action{System.Int64,``0,``1,``2,``3},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.For``5(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.For``5(System.Int32,System.Int32,System.Action{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.For``5(System.Int64,System.Int64,System.Action{System.Int64,``0,``1,``2,``3,``4},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.For``5(System.Int64,System.Int64,System.Action{System.Int64,``0,``1,``2,``3,``4},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``4(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForAsync``5(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEach``4(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3},``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEach``5(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3,``4},``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEach``6(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEach``6(System.Collections.Generic.IEnumerable{``0},System.Action{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``4(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task},``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``5(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task},``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``6(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachAsync``6(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResult``4(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3},``1,``2,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResult``5(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4},``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResult``6(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5},``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResult``7(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResult``7(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5,``6},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``4(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``1,``2,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``5(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``6(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``7(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task{``6}},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForEachResultAsync``7(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1,``2,``3,``4,``5,System.Threading.CancellationToken,System.Threading.Tasks.Task{``6}},``1,``2,``3,``4,``5,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``6}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResult``4(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3},``0,``1,``2,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResult``4(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3},``0,``1,``2,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResult``5(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResult``5(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResult``5(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResult``5(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResult``6(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResult``6(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResult``6(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResult``6(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncTaskFactoryOptions})~System.Collections.Generic.IReadOnlyCollection{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``4(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``4(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``3}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``5(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``4}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(System.Int32,System.Int32,System.Func{System.Int32,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "member", Target = "~M:Cuemon.Threading.ParallelFactory.ForResultAsync``6(System.Int64,System.Int64,System.Func{System.Int64,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Action{Cuemon.Threading.AsyncWorkloadOptions})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{``5}}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments.", Scope = "type", Target = "~T:Cuemon.Threading.FuncWhileSynchronousLoop`3")] diff --git a/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs b/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs index 1ab4b66e4..7061aaba9 100644 --- a/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs +++ b/src/Cuemon.Threading/ParallelFactory.ForEachAsync.cs @@ -132,6 +132,7 @@ private static async Task ForEachCoreAsync(IEnumerable(source, options.PartitionSize); + while (partitioner.HasPartitions) { var queue = new List(); From 0fb36cb9c6cff8fdcb034a6bff3ed90dad6ad018 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 02:50:51 +0200 Subject: [PATCH 167/385] Upadted NuGet information. --- src/Cuemon.Threading/Cuemon.Threading.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Threading/Cuemon.Threading.csproj b/src/Cuemon.Threading/Cuemon.Threading.csproj index 85995d628..603e99389 100644 --- a/src/Cuemon.Threading/Cuemon.Threading.csproj +++ b/src/Cuemon.Threading/Cuemon.Threading.csproj @@ -9,7 +9,7 @@ Cuemon.Threading Cuemon.Threading The Cuemon.Threading namespace contains types that can prove helpful when working with concurrent operations. The namespace relates to the System.Threading namespace. - parallel-factory for-async for-each-async for-each-result-async for-result-async while-async while-result-async + advanced-parallel-factory parallel-factory for-async for-each-async for-each-result-async for-result-async while-async while-result-async async-options timer-factory for-loop-ruleset diff --git a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt index 04306ea5c..91562d727 100644 --- a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt @@ -1,12 +1,27 @@ Version: 6.0.0 Availability: NET Standard 2.0   +# Upgrade Steps +- The extent of refactoring applied to this project resulted in so many breaking changes that a git diff is advisable +  # New Features -- ADDED TimerFactory class in the Cuemon.Threading namespace that provides access to factory methods for creating and configuring Timer instances - ADDED AsyncOptions class in the Cuemon.Threading namespace that specifies options that is related to asynchronous operations +- ADDED AsyncTaskFactoryOptions class in the Cuemon.Threading namespace that specifies options that is related to both ParallelFactory and AdvancedParallelFactory +- ADDED AsyncWorkloadOptions class in the Cuemon.Threading namespace that specifies options that is related to both ParallelFactory and AdvancedParallelFactory +- ADDED AdvancedParallelFactory static class in the Cuemon.Threading namespace that provides a factory based way to work with advanced scenarios that encapsulate and re-use existing code while adding support for typically long-running parallel loops and regions +- ADDED TimerFactory class in the Cuemon.Threading namespace that provides access to factory methods for creating and configuring Timer instances +- ADDED ForLoopRuleset class in the Cuemon.Threading namespace that specifies the rules of a for-loop control flow statement +  +# Breaking Changes +- REMOVED ThreadPoolUtility class from the Cuemon.Threading namespace   # Bug Fixes - APPLIED ConfigureAwait(false) to all async methods +- FIXED a bug that would lead to endless loop if workload was 1 (PartitionSize) +  +# Improvements +- All members now support true async functionality ForAsync, ForResultAsync, WhileAsync, WhileResultAsync, ForEachAsync and ForEachResultAsync +- Advanced members was moved to the AdvancedParallelFactory class to conform to Framework Design Guidelines   # Quality Analysis Actions - APPLIED while loop over for loop https://rules.sonarsource.com/csharp/RSPEC-1264 From b683183d4329a262ca7399d2781199606df4ddf9 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 03:02:50 +0200 Subject: [PATCH 168/385] Bugfix in test due to other bugfix in TaskFuncFactory and TaskActionFactory (if (ct.IsCancellationRequested) { throw new TaskCanceledException(); }) --- test/Cuemon.Core.Tests/DisposableTest.cs | 39 +++++++++++++----------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/test/Cuemon.Core.Tests/DisposableTest.cs b/test/Cuemon.Core.Tests/DisposableTest.cs index bc073e6b1..b3643d537 100644 --- a/test/Cuemon.Core.Tests/DisposableTest.cs +++ b/test/Cuemon.Core.Tests/DisposableTest.cs @@ -95,26 +95,29 @@ public async Task SafeInvokeAsync_ShouldAbideRuleCA2000() Assert.Null(stream); Assert.Throws(() => msRef.Length); - var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(5)); - msRef = null; - called = 0; - stream = await Disposable.SafeInvokeAsync(() => new MemoryStream(), async (ms, g, ct) => - { - msRef = ms; - Assert.Equal(guid, g); - await Task.Delay(TimeSpan.FromSeconds(1)); - await ms.WriteAsync(new byte[] { 1 }, ct); - ms.Position = 0; - return ms; - }, guid, ctsShouldFail.Token, (exception, g, ct) => + await Assert.ThrowsAsync(async () => { - Assert.Equal(guid, g); - Assert.True(exception is TaskCanceledException); - return Task.CompletedTask; + var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(5)); + msRef = null; + called = 0; + stream = await Disposable.SafeInvokeAsync(() => new MemoryStream(), async (ms, g, ct) => + { + msRef = ms; + Assert.Equal(guid, g); + await Task.Delay(TimeSpan.FromSeconds(1)); + await ms.WriteAsync(new byte[] {1}, ct); + ms.Position = 0; + return ms; + }, guid, ctsShouldFail.Token, (exception, g, ct) => + { + Assert.Equal(guid, g); + Assert.True(exception is TaskCanceledException); + return Task.CompletedTask; + }); + Assert.Equal(0, called); + Assert.Null(stream); + Assert.Throws(() => msRef.Length); }); - Assert.Equal(0, called); - Assert.Null(stream); - Assert.Throws(() => msRef.Length); stream = await Disposable.SafeInvokeAsync(() => new MemoryStream(), async (ms, n1, n2, n3, n4, n5, ct) => { From f663ea8d7d9069477c2b5d6deebb1c4900b1eee4 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 11 Sep 2020 09:32:14 +0200 Subject: [PATCH 169/385] Update azure-pipelines.yml for Azure Pipelines Problems with WhiteSource; removed excluded test folder. --- azure-pipelines.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index eb8bed3ed..ed603b7c4 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -160,9 +160,6 @@ jobs: - task: WhiteSource Bolt@20 condition: eq(variables['Agent.OS'], 'Windows_NT') displayName: 'WhiteSource Bolt' - inputs: - advance: true - exclude: 'test' - task: PublishBuildArtifacts@1 condition: eq(variables['Agent.OS'], 'Windows_NT') From 941ec5dec2d0703d4524aa7aafc5dcaca2f4efec Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 11 Sep 2020 11:26:39 +0200 Subject: [PATCH 170/385] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ed603b7c4..285bfff66 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -10,7 +10,7 @@ variables: jobs: - job: CI - timeoutInMinutes: 75 + timeoutInMinutes: 120 strategy: matrix: From 76121ec9c2e8d597c940af3dbd80375ae3a05934 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 15:07:57 +0200 Subject: [PATCH 171/385] Unit tests fails randomly on Linux; need to investigate reason. --- .../ParallelFactoryAsyncTest.cs | 86 +++++++++++++++---- .../ParallelFactoryTest.cs | 86 +++++++++++++++---- 2 files changed, 135 insertions(+), 37 deletions(-) diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs index daffbba2b..3030b73f2 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs @@ -1,4 +1,5 @@ -using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -11,6 +12,9 @@ namespace Cuemon.Threading { public class ParallelFactoryAsyncTest : Test { + private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(15)); + private readonly int _extremePartitionSize = 2048; + public ParallelFactoryAsyncTest(ITestOutputHelper output) : base(output) { } @@ -26,7 +30,11 @@ await ParallelFactory.ForAsync(0, count, async (i, ct) => { await Task.Delay(50, ct); cb.Add(i); - }, o => o.PartitionSize = 64); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = 64; + }); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -66,7 +74,7 @@ await ParallelFactory.ForAsync(0, count, async (i, ct) => { await Task.Delay(1000, ct); cb.Add(i); - }); + }, o => o.CancellationToken = _cts.Token); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -83,7 +91,7 @@ await ParallelFactory.ForAsync(0, count, async (i, ct) => { await Task.Delay(100, ct); cb.Add(i); - }, o => o.PartitionSize = 4096); + }, o => o.PartitionSize = _extremePartitionSize); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -100,7 +108,11 @@ public async Task ForResultAsync_ShouldRunConcurrent() await Task.Delay(50, ct); cb.Add(i); return i; - }, o => o.PartitionSize = 64); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = 64; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -141,7 +153,7 @@ public async Task ForResultAsync_ShouldRunConcurrent_LongRunning_SystemPartition await Task.Delay(1000, ct); cb.Add(i); return i; - }); + }, o => o.CancellationToken = _cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -158,7 +170,11 @@ public async Task ForResultAsync_ShouldRunConcurrent_LongRunning_ExtremePartitio await Task.Delay(100, ct); cb.Add(i); return i; - }, o => o.PartitionSize = 4096); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = _extremePartitionSize; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -175,7 +191,11 @@ await ParallelFactory.ForEachAsync(ic, async (i, ct) => { await Task.Delay(50, ct); cb.Add(i); - }, o => o.PartitionSize = 64); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = 64; + }); Assert.Equal(count, cb.Count); Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); @@ -216,7 +236,7 @@ await ParallelFactory.ForEachAsync(ic, async (i, ct) => { await Task.Delay(1000, ct); cb.Add(i); - }); + }, o => o.CancellationToken = _cts.Token); Assert.Equal(count, cb.Count); Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); @@ -233,7 +253,11 @@ await ParallelFactory.ForEachAsync(ic, async (i, ct) => { await Task.Delay(100, ct); cb.Add(i); - }, o => o.PartitionSize = 4096); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = _extremePartitionSize; + }); Assert.Equal(count, cb.Count); Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); @@ -251,7 +275,11 @@ public async Task ForEachResultAsync_ShouldRunConcurrent() await Task.Delay(50, ct); cb.Add(i); return i; - }, o => o.PartitionSize = 64); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = 64; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -294,7 +322,7 @@ public async Task ForEachResultAsync_ShouldRunConcurrent_LongRunning_SystemParti await Task.Delay(1000, ct); cb.Add(i); return i; - }); + }, o => o.CancellationToken = _cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -312,7 +340,11 @@ public async Task ForEachResultAsync_ShouldRunConcurrent_LongRunning_ExtremePart await Task.Delay(100, ct); cb.Add(i); return i; - }, o => o.PartitionSize = 4096); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = _extremePartitionSize; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -330,7 +362,11 @@ await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(ou { await Task.Delay(50, ct); cb.Add(i); - }, o => o.PartitionSize = 64); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = 64; + }); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -373,7 +409,7 @@ await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(ou { await Task.Delay(1000, ct); cb.Add(i); - }); + }, o => o.CancellationToken = _cts.Token); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -391,7 +427,11 @@ await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(ou { await Task.Delay(100, ct); cb.Add(i); - }, o => o.PartitionSize = 4096); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = _extremePartitionSize; + }); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -410,7 +450,11 @@ public async Task WhileResultAsync_ShouldRunConcurrent() await Task.Delay(50, ct); cb.Add(i); return i; - }, o => o.PartitionSize = 64); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = 64; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -455,7 +499,7 @@ public async Task WhileResultAsync_ShouldRunConcurrent_LongRunning_SystemPartiti await Task.Delay(1000, ct); cb.Add(i); return i; - }); + }, o => o.CancellationToken = _cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -474,7 +518,11 @@ public async Task WhileResultAsync_ShouldRunConcurrent_LongRunning_ExtremePartit await Task.Delay(100, ct); cb.Add(i); return i; - }, o => o.PartitionSize = 4096); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = _extremePartitionSize; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs index 24d984eb1..27e7b2b5e 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs @@ -5,7 +5,6 @@ using System.Threading; using System.Threading.Tasks; using Cuemon.Extensions.Xunit; -using Microsoft.VisualStudio.TestPlatform.Utilities; using Xunit; using Xunit.Abstractions; @@ -13,6 +12,9 @@ namespace Cuemon.Threading { public class ParallelFactoryTest : Test { + private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(15)); + private readonly int _extremePartitionSize = 2048; + public ParallelFactoryTest(ITestOutputHelper output) : base(output) { } @@ -28,7 +30,11 @@ public void For_ShouldRunConcurrent() { Thread.Sleep(50); cb.Add(i); - }, o => o.CreationOptions = TaskCreationOptions.None); + }, o => + { + o.CancellationToken = _cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -83,7 +89,7 @@ public void For_ShouldRunConcurrent_LongRunning_SystemPartition() { Thread.Sleep(1000); cb.Add(i); - }); + }, o => o.CancellationToken = _cts.Token); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -100,7 +106,11 @@ public void For_ShouldRunConcurrent_LongRunning_ExtremePartition() { Thread.Sleep(1); cb.Add(i); - }, o => o.PartitionSize = 4096); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = _extremePartitionSize; + }); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -117,7 +127,11 @@ public void ForResult_ShouldRunConcurrent() Thread.Sleep(50); cb.Add(i); return i; - }, o => o.CreationOptions = TaskCreationOptions.None); + }, o => + { + o.CancellationToken = _cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -173,7 +187,7 @@ public void ForResult_ShouldRunConcurrent_LongRunning_SystemPartition() Thread.Sleep(1000); cb.Add(i); return i; - }); + }, o => o.CancellationToken = _cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -190,7 +204,11 @@ public void ForResult_ShouldRunConcurrent_LongRunning_ExtremePartition() Thread.Sleep(100); cb.Add(i); return i; - }, o => o.PartitionSize = 4096); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = _extremePartitionSize; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -207,7 +225,11 @@ public void ForEach_ShouldRunConcurrent() { Thread.Sleep(50); cb.Add(i); - }, o => o.CreationOptions = TaskCreationOptions.None); + }, o => + { + o.CancellationToken = _cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); Assert.Equal(count, cb.Count); Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); @@ -263,7 +285,7 @@ public void ForEach_ShouldRunConcurrent_LongRunning_SystemPartition() { Thread.Sleep(1000); cb.Add(i); - }); + }, o => o.CancellationToken = _cts.Token); Assert.Equal(count, cb.Count); Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); @@ -280,7 +302,11 @@ public void ForEach_ShouldRunConcurrent_LongRunning_ExtremePartition() { Thread.Sleep(100); cb.Add(i); - }, o => o.PartitionSize = 4096); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = _extremePartitionSize; + }); Assert.Equal(count, cb.Count); Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); @@ -298,7 +324,11 @@ public void ForEachResult_ShouldRunConcurrent() Thread.Sleep(50); cb.Add(i); return i; - }, o => o.CreationOptions = TaskCreationOptions.None); + }, o => + { + o.CancellationToken = _cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -358,7 +388,7 @@ public void ForEachResult_ShouldRunConcurrent_LongRunning_SystemPartition() Thread.Sleep(1000); cb.Add(i); return i; - }); + }, o => o.CancellationToken = _cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -376,7 +406,11 @@ public void ForEachResult_ShouldRunConcurrent_LongRunning_ExtremePartition() Thread.Sleep(100); cb.Add(i); return i; - }, o => o.PartitionSize = 4096); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = _extremePartitionSize; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -394,7 +428,11 @@ public void While_ShouldRunConcurrent() { Thread.Sleep(50); cb.Add(i); - }, o => o.CreationOptions = TaskCreationOptions.None); + }, o => + { + o.CancellationToken = _cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -470,7 +508,11 @@ public void While_ShouldRunConcurrent_LongRunning_ExtremePartition() { Thread.Sleep(100); cb.Add(i); - }, o => o.PartitionSize = 4096); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = _extremePartitionSize; + }); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -489,7 +531,11 @@ public void WhileResult_ShouldRunConcurrent() Thread.Sleep(50); cb.Add(i); return i; - }, o => o.CreationOptions = TaskCreationOptions.None); + }, o => + { + o.CancellationToken = _cts.Token; + o.CreationOptions = TaskCreationOptions.None; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -549,7 +595,7 @@ public void WhileResult_ShouldRunConcurrent_LongRunning_SystemPartition() Thread.Sleep(1000); cb.Add(i); return i; - }); + }, o => o.CancellationToken = _cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -568,7 +614,11 @@ public void WhileResult_ShouldRunConcurrent_LongRunning_ExtremePartition() Thread.Sleep(100); cb.Add(i); return i; - }, o => o.PartitionSize = 4096); + }, o => + { + o.CancellationToken = _cts.Token; + o.PartitionSize = _extremePartitionSize; + }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); From c8c1a7aaf4d609c7d4f0b4711c81816c363972c1 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 15:56:18 +0200 Subject: [PATCH 172/385] Removed legacy impl. needed for dotnetcore 1 --- src/Cuemon.Core/Globalization/World.cs | 30 ++------------------------ 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/Cuemon.Core/Globalization/World.cs b/src/Cuemon.Core/Globalization/World.cs index c63fddafa..c27c7e155 100644 --- a/src/Cuemon.Core/Globalization/World.cs +++ b/src/Cuemon.Core/Globalization/World.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; -using Cuemon.Reflection; namespace Cuemon.Globalization { @@ -16,33 +14,9 @@ public static class World { var cultures = new SortedList(); var specificCultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures); - if (specificCultures != null) + foreach (var c in specificCultures) { - foreach (var c in specificCultures) - { - cultures.Add(c.DisplayName, c); - } - return cultures.Values; - } - - using (var lfdSpecificCultures = Decorator.Enclose(typeof(World).Assembly).GetManifestResources("CultureInfo.SpecificCultures.dsv", ManifestResourceMatch.ContainsName).Values.Single()) - { - using (var reader = new StreamReader(lfdSpecificCultures)) - { - string specificCulture; - while ((specificCulture = reader.ReadLine()) != null) - { - try - { - var c = new CultureInfo(specificCulture); - cultures.Add(c.DisplayName, c); - } - catch (CultureNotFoundException) - { - // ignored on systems not supporting the specificCulture - } - } - } + cultures.Add(c.DisplayName, c); } return cultures.Values; }); From 66fccd07b63b789700aa68d7cf4eab44127296e5 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 15:58:02 +0200 Subject: [PATCH 173/385] Removed file for legacy impl. needed for dotnetcore 1 --- src/Cuemon.Core/Cuemon.Core.csproj | 4 - .../CultureInfo.SpecificCultures.dsv | 569 ------------------ 2 files changed, 573 deletions(-) delete mode 100644 src/Cuemon.Core/Globalization/CultureInfo.SpecificCultures.dsv diff --git a/src/Cuemon.Core/Cuemon.Core.csproj b/src/Cuemon.Core/Cuemon.Core.csproj index 96fbd1f0d..7c235c1be 100644 --- a/src/Cuemon.Core/Cuemon.Core.csproj +++ b/src/Cuemon.Core/Cuemon.Core.csproj @@ -13,8 +13,4 @@ action-factory bit-unit byte-unit calculator configure configure-revert configure-exchange configurable condition options-pattern data-reader decorator delimited-string disposable finalize-disposable safe-invoke safe-invoke-async func-factory patterns reference-project clean-architecture clean-code task-action-factory task-func-factory template template-factory time-range time-unit validator guard text-encoding parser-factory security aes-cryptor cyclic-redundancy-check fowler-noll-vo-hash hash-factory hash-result hmac-message-digest hmac-secure-hash-algorithm keyed-crypto-hash keyed-crypto-algorithm message-digest non-crypto-algorithm secure-hash-algorithm unkeyed-crypto-hash - - - - \ No newline at end of file diff --git a/src/Cuemon.Core/Globalization/CultureInfo.SpecificCultures.dsv b/src/Cuemon.Core/Globalization/CultureInfo.SpecificCultures.dsv deleted file mode 100644 index bd581ecd9..000000000 --- a/src/Cuemon.Core/Globalization/CultureInfo.SpecificCultures.dsv +++ /dev/null @@ -1,569 +0,0 @@ -aa-DJ -aa-ER -aa-ET -af-NA -af-ZA -agq-CM -ak-GH -am-ET -ar-001 -ar-AE -ar-BH -ar-DJ -ar-DZ -ar-EG -ar-ER -ar-IL -ar-IQ -ar-JO -ar-KM -ar-KW -ar-LB -ar-LY -ar-MA -ar-MR -ar-OM -ar-PS -ar-QA -ar-SA -ar-SD -ar-SO -ar-SS -ar-SY -ar-TD -ar-TN -ar-YE -arn-CL -as-IN -asa-TZ -ast-ES -az-Cyrl-AZ -az-Latn-AZ -ba-RU -bas-CM -be-BY -bem-ZM -bez-TZ -bg-BG -bin-NG -bm-Latn-ML -bn-BD -bn-IN -bo-CN -bo-IN -br-FR -brx-IN -bs-Cyrl-BA -bs-Latn-BA -byn-ER -ca-AD -ca-ES -ca-ES-valencia -ca-FR -ca-IT -ce-RU -cgg-UG -chr-Cher-US -co-FR -cs-CZ -cu-RU -cy-GB -da-DK -da-GL -dav-KE -de-AT -de-BE -de-CH -de-DE -de-IT -de-LI -de-LU -dje-NE -dsb-DE -dua-CM -dv-MV -dyo-SN -dz-BT -ebu-KE -ee-GH -ee-TG -el-CY -el-GR -en-001 -en-029 -en-150 -en-AG -en-AI -en-AS -en-AT -en-AU -en-BB -en-BE -en-BI -en-BM -en-BS -en-BW -en-BZ -en-CA -en-CC -en-CH -en-CK -en-CM -en-CX -en-CY -en-DE -en-DK -en-DM -en-ER -en-FI -en-FJ -en-FK -en-FM -en-GB -en-GD -en-GG -en-GH -en-GI -en-GM -en-GU -en-GY -en-HK -en-ID -en-IE -en-IL -en-IM -en-IN -en-IO -en-JE -en-JM -en-KE -en-KI -en-KN -en-KY -en-LC -en-LR -en-LS -en-MG -en-MH -en-MO -en-MP -en-MS -en-MT -en-MU -en-MW -en-MY -en-NA -en-NF -en-NG -en-NL -en-NR -en-NU -en-NZ -en-PG -en-PH -en-PK -en-PN -en-PR -en-PW -en-RW -en-SB -en-SC -en-SD -en-SE -en-SG -en-SH -en-SI -en-SL -en-SS -en-SX -en-SZ -en-TC -en-TK -en-TO -en-TT -en-TV -en-TZ -en-UG -en-UM -en-US -en-VC -en-VG -en-VI -en-VU -en-WS -en-ZA -en-ZM -en-ZW -eo-001 -es-419 -es-AR -es-BO -es-BR -es-BZ -es-CL -es-CO -es-CR -es-CU -es-DO -es-EC -es-ES -es-GQ -es-GT -es-HN -es-MX -es-NI -es-PA -es-PE -es-PH -es-PR -es-PY -es-SV -es-US -es-UY -es-VE -et-EE -eu-ES -ewo-CM -fa-IR -ff-Latn-BF -ff-Latn-CM -ff-Latn-GH -ff-Latn-GM -ff-Latn-GN -ff-Latn-GW -ff-Latn-LR -ff-Latn-MR -ff-Latn-NE -ff-Latn-NG -ff-Latn-SL -ff-Latn-SN -fi-FI -fil-PH -fo-DK -fo-FO -fr-029 -fr-BE -fr-BF -fr-BI -fr-BJ -fr-BL -fr-CA -fr-CD -fr-CF -fr-CG -fr-CH -fr-CI -fr-CM -fr-DJ -fr-DZ -fr-FR -fr-GA -fr-GF -fr-GN -fr-GP -fr-GQ -fr-HT -fr-KM -fr-LU -fr-MA -fr-MC -fr-MF -fr-MG -fr-ML -fr-MQ -fr-MR -fr-MU -fr-NC -fr-NE -fr-PF -fr-PM -fr-RE -fr-RW -fr-SC -fr-SN -fr-SY -fr-TD -fr-TG -fr-TN -fr-VU -fr-WF -fr-YT -fur-IT -fy-NL -ga-IE -gd-GB -gl-ES -gn-PY -gsw-CH -gsw-FR -gsw-LI -gu-IN -guz-KE -gv-IM -ha-Latn-GH -ha-Latn-NE -ha-Latn-NG -haw-US -he-IL -hi-IN -hr-BA -hr-HR -hsb-DE -hu-HU -hy-AM -ia-001 -ibb-NG -id-ID -ig-NG -ii-CN -is-IS -it-CH -it-IT -it-SM -it-VA -iu-Cans-CA -iu-Latn-CA -ja-JP -jgo-CM -jmc-TZ -jv-Java-ID -jv-Latn-ID -ka-GE -kab-DZ -kam-KE -kde-TZ -kea-CV -khq-ML -ki-KE -kk-KZ -kkj-CM -kl-GL -kln-KE -km-KH -kn-IN -ko-KP -ko-KR -kok-IN -kr-Latn-NG -ks-Arab-IN -ks-Deva-IN -ksb-TZ -ksf-CM -ksh-DE -ku-Arab-IQ -ku-Arab-IR -kw-GB -ky-KG -la-001 -lag-TZ -lb-LU -lg-UG -lkt-US -ln-AO -ln-CD -ln-CF -ln-CG -lo-LA -lrc-IQ -lrc-IR -lt-LT -lu-CD -luo-KE -luy-KE -lv-LV -mas-KE -mas-TZ -mer-KE -mfe-MU -mg-MG -mgh-MZ -mgo-CM -mi-NZ -mk-MK -ml-IN -mn-MN -mn-Mong-CN -mn-Mong-MN -mni-IN -moh-CA -mr-IN -ms-BN -ms-MY -ms-SG -mt-MT -mua-CM -my-MM -mzn-IR -naq-NA -nb-NO -nb-SJ -nd-ZW -nds-DE -nds-NL -ne-IN -ne-NP -nl-AW -nl-BE -nl-BQ -nl-CW -nl-NL -nl-SR -nl-SX -nmg-CM -nn-NO -nnh-CM -nqo-GN -nr-ZA -nso-ZA -nus-SS -nyn-UG -oc-FR -om-ET -om-KE -or-IN -os-GE -os-RU -pa-Arab-PK -pa-IN -pap-029 -pl-PL -prg-001 -prs-AF -ps-AF -pt-AO -pt-BR -pt-CH -pt-CV -pt-GQ -pt-GW -pt-LU -pt-MO -pt-MZ -pt-PT -pt-ST -pt-TL -quc-Latn-GT -quz-BO -quz-EC -quz-PE -rm-CH -rn-BI -ro-MD -ro-RO -rof-TZ -ru-BY -ru-KG -ru-KZ -ru-MD -ru-RU -ru-UA -rw-RW -rwk-TZ -sa-IN -sah-RU -saq-KE -sbp-TZ -sd-Arab-PK -sd-Deva-IN -se-FI -se-NO -se-SE -seh-MZ -ses-ML -sg-CF -shi-Latn-MA -shi-Tfng-MA -si-LK -sk-SK -sl-SI -sma-NO -sma-SE -smj-NO -smj-SE -smn-FI -sms-FI -sn-Latn-ZW -so-DJ -so-ET -so-KE -so-SO -sq-AL -sq-MK -sq-XK -sr-Cyrl-BA -sr-Cyrl-ME -sr-Cyrl-RS -sr-Cyrl-XK -sr-Latn-BA -sr-Latn-ME -sr-Latn-RS -sr-Latn-XK -ss-SZ -ss-ZA -ssy-ER -st-LS -st-ZA -sv-AX -sv-FI -sv-SE -sw-CD -sw-KE -sw-TZ -sw-UG -syr-SY -ta-IN -ta-LK -ta-MY -ta-SG -te-IN -teo-KE -teo-UG -tg-Cyrl-TJ -th-TH -ti-ER -ti-ET -tig-ER -tk-TM -tn-BW -tn-ZA -to-TO -tr-CY -tr-TR -ts-ZA -tt-RU -twq-NE -tzm-Arab-MA -tzm-Latn-DZ -tzm-Latn-MA -tzm-Tfng-MA -ug-CN -uk-UA -ur-IN -ur-PK -uz-Arab-AF -uz-Cyrl-UZ -uz-Latn-UZ -vai-Latn-LR -vai-Vaii-LR -ve-ZA -vi-VN -vo-001 -vun-TZ -wae-CH -wal-ET -wo-SN -xh-ZA -xog-UG -yav-CM -yi-001 -yo-BJ -yo-NG -zgh-Tfng-MA -zh-CN -zh-Hans-HK -zh-Hans-MO -zh-HK -zh-MO -zh-SG -zh-TW -zu-ZA \ No newline at end of file From 0beb2a641ae802024c11898de22e5fdc074b33c4 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 16:30:38 +0200 Subject: [PATCH 174/385] Reduced to max. 2 minutes (to figure out if OS is to slow) --- test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs | 2 +- test/Cuemon.Threading.Tests/ParallelFactoryTest.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs index 3030b73f2..6ef4f59d1 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs @@ -12,7 +12,7 @@ namespace Cuemon.Threading { public class ParallelFactoryAsyncTest : Test { - private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(15)); + private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(2)); private readonly int _extremePartitionSize = 2048; public ParallelFactoryAsyncTest(ITestOutputHelper output) : base(output) diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs index 27e7b2b5e..01320ae0c 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs @@ -12,7 +12,7 @@ namespace Cuemon.Threading { public class ParallelFactoryTest : Test { - private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(15)); + private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(2)); private readonly int _extremePartitionSize = 2048; public ParallelFactoryTest(ITestOutputHelper output) : base(output) From 18a47f523be258423106b9fc1627e51c7b5cd2a2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 18:23:40 +0200 Subject: [PATCH 175/385] Added removed file to unit test project. --- .../Assets/CultureInfo.SpecificCultures.dsv | 569 ++++++++++++++++++ .../Cuemon.Core.Tests.csproj | 8 + .../AssemblyDecoratorExtensionsTest.cs | 5 +- 3 files changed, 580 insertions(+), 2 deletions(-) create mode 100644 test/Cuemon.Core.Tests/Assets/CultureInfo.SpecificCultures.dsv diff --git a/test/Cuemon.Core.Tests/Assets/CultureInfo.SpecificCultures.dsv b/test/Cuemon.Core.Tests/Assets/CultureInfo.SpecificCultures.dsv new file mode 100644 index 000000000..bd581ecd9 --- /dev/null +++ b/test/Cuemon.Core.Tests/Assets/CultureInfo.SpecificCultures.dsv @@ -0,0 +1,569 @@ +aa-DJ +aa-ER +aa-ET +af-NA +af-ZA +agq-CM +ak-GH +am-ET +ar-001 +ar-AE +ar-BH +ar-DJ +ar-DZ +ar-EG +ar-ER +ar-IL +ar-IQ +ar-JO +ar-KM +ar-KW +ar-LB +ar-LY +ar-MA +ar-MR +ar-OM +ar-PS +ar-QA +ar-SA +ar-SD +ar-SO +ar-SS +ar-SY +ar-TD +ar-TN +ar-YE +arn-CL +as-IN +asa-TZ +ast-ES +az-Cyrl-AZ +az-Latn-AZ +ba-RU +bas-CM +be-BY +bem-ZM +bez-TZ +bg-BG +bin-NG +bm-Latn-ML +bn-BD +bn-IN +bo-CN +bo-IN +br-FR +brx-IN +bs-Cyrl-BA +bs-Latn-BA +byn-ER +ca-AD +ca-ES +ca-ES-valencia +ca-FR +ca-IT +ce-RU +cgg-UG +chr-Cher-US +co-FR +cs-CZ +cu-RU +cy-GB +da-DK +da-GL +dav-KE +de-AT +de-BE +de-CH +de-DE +de-IT +de-LI +de-LU +dje-NE +dsb-DE +dua-CM +dv-MV +dyo-SN +dz-BT +ebu-KE +ee-GH +ee-TG +el-CY +el-GR +en-001 +en-029 +en-150 +en-AG +en-AI +en-AS +en-AT +en-AU +en-BB +en-BE +en-BI +en-BM +en-BS +en-BW +en-BZ +en-CA +en-CC +en-CH +en-CK +en-CM +en-CX +en-CY +en-DE +en-DK +en-DM +en-ER +en-FI +en-FJ +en-FK +en-FM +en-GB +en-GD +en-GG +en-GH +en-GI +en-GM +en-GU +en-GY +en-HK +en-ID +en-IE +en-IL +en-IM +en-IN +en-IO +en-JE +en-JM +en-KE +en-KI +en-KN +en-KY +en-LC +en-LR +en-LS +en-MG +en-MH +en-MO +en-MP +en-MS +en-MT +en-MU +en-MW +en-MY +en-NA +en-NF +en-NG +en-NL +en-NR +en-NU +en-NZ +en-PG +en-PH +en-PK +en-PN +en-PR +en-PW +en-RW +en-SB +en-SC +en-SD +en-SE +en-SG +en-SH +en-SI +en-SL +en-SS +en-SX +en-SZ +en-TC +en-TK +en-TO +en-TT +en-TV +en-TZ +en-UG +en-UM +en-US +en-VC +en-VG +en-VI +en-VU +en-WS +en-ZA +en-ZM +en-ZW +eo-001 +es-419 +es-AR +es-BO +es-BR +es-BZ +es-CL +es-CO +es-CR +es-CU +es-DO +es-EC +es-ES +es-GQ +es-GT +es-HN +es-MX +es-NI +es-PA +es-PE +es-PH +es-PR +es-PY +es-SV +es-US +es-UY +es-VE +et-EE +eu-ES +ewo-CM +fa-IR +ff-Latn-BF +ff-Latn-CM +ff-Latn-GH +ff-Latn-GM +ff-Latn-GN +ff-Latn-GW +ff-Latn-LR +ff-Latn-MR +ff-Latn-NE +ff-Latn-NG +ff-Latn-SL +ff-Latn-SN +fi-FI +fil-PH +fo-DK +fo-FO +fr-029 +fr-BE +fr-BF +fr-BI +fr-BJ +fr-BL +fr-CA +fr-CD +fr-CF +fr-CG +fr-CH +fr-CI +fr-CM +fr-DJ +fr-DZ +fr-FR +fr-GA +fr-GF +fr-GN +fr-GP +fr-GQ +fr-HT +fr-KM +fr-LU +fr-MA +fr-MC +fr-MF +fr-MG +fr-ML +fr-MQ +fr-MR +fr-MU +fr-NC +fr-NE +fr-PF +fr-PM +fr-RE +fr-RW +fr-SC +fr-SN +fr-SY +fr-TD +fr-TG +fr-TN +fr-VU +fr-WF +fr-YT +fur-IT +fy-NL +ga-IE +gd-GB +gl-ES +gn-PY +gsw-CH +gsw-FR +gsw-LI +gu-IN +guz-KE +gv-IM +ha-Latn-GH +ha-Latn-NE +ha-Latn-NG +haw-US +he-IL +hi-IN +hr-BA +hr-HR +hsb-DE +hu-HU +hy-AM +ia-001 +ibb-NG +id-ID +ig-NG +ii-CN +is-IS +it-CH +it-IT +it-SM +it-VA +iu-Cans-CA +iu-Latn-CA +ja-JP +jgo-CM +jmc-TZ +jv-Java-ID +jv-Latn-ID +ka-GE +kab-DZ +kam-KE +kde-TZ +kea-CV +khq-ML +ki-KE +kk-KZ +kkj-CM +kl-GL +kln-KE +km-KH +kn-IN +ko-KP +ko-KR +kok-IN +kr-Latn-NG +ks-Arab-IN +ks-Deva-IN +ksb-TZ +ksf-CM +ksh-DE +ku-Arab-IQ +ku-Arab-IR +kw-GB +ky-KG +la-001 +lag-TZ +lb-LU +lg-UG +lkt-US +ln-AO +ln-CD +ln-CF +ln-CG +lo-LA +lrc-IQ +lrc-IR +lt-LT +lu-CD +luo-KE +luy-KE +lv-LV +mas-KE +mas-TZ +mer-KE +mfe-MU +mg-MG +mgh-MZ +mgo-CM +mi-NZ +mk-MK +ml-IN +mn-MN +mn-Mong-CN +mn-Mong-MN +mni-IN +moh-CA +mr-IN +ms-BN +ms-MY +ms-SG +mt-MT +mua-CM +my-MM +mzn-IR +naq-NA +nb-NO +nb-SJ +nd-ZW +nds-DE +nds-NL +ne-IN +ne-NP +nl-AW +nl-BE +nl-BQ +nl-CW +nl-NL +nl-SR +nl-SX +nmg-CM +nn-NO +nnh-CM +nqo-GN +nr-ZA +nso-ZA +nus-SS +nyn-UG +oc-FR +om-ET +om-KE +or-IN +os-GE +os-RU +pa-Arab-PK +pa-IN +pap-029 +pl-PL +prg-001 +prs-AF +ps-AF +pt-AO +pt-BR +pt-CH +pt-CV +pt-GQ +pt-GW +pt-LU +pt-MO +pt-MZ +pt-PT +pt-ST +pt-TL +quc-Latn-GT +quz-BO +quz-EC +quz-PE +rm-CH +rn-BI +ro-MD +ro-RO +rof-TZ +ru-BY +ru-KG +ru-KZ +ru-MD +ru-RU +ru-UA +rw-RW +rwk-TZ +sa-IN +sah-RU +saq-KE +sbp-TZ +sd-Arab-PK +sd-Deva-IN +se-FI +se-NO +se-SE +seh-MZ +ses-ML +sg-CF +shi-Latn-MA +shi-Tfng-MA +si-LK +sk-SK +sl-SI +sma-NO +sma-SE +smj-NO +smj-SE +smn-FI +sms-FI +sn-Latn-ZW +so-DJ +so-ET +so-KE +so-SO +sq-AL +sq-MK +sq-XK +sr-Cyrl-BA +sr-Cyrl-ME +sr-Cyrl-RS +sr-Cyrl-XK +sr-Latn-BA +sr-Latn-ME +sr-Latn-RS +sr-Latn-XK +ss-SZ +ss-ZA +ssy-ER +st-LS +st-ZA +sv-AX +sv-FI +sv-SE +sw-CD +sw-KE +sw-TZ +sw-UG +syr-SY +ta-IN +ta-LK +ta-MY +ta-SG +te-IN +teo-KE +teo-UG +tg-Cyrl-TJ +th-TH +ti-ER +ti-ET +tig-ER +tk-TM +tn-BW +tn-ZA +to-TO +tr-CY +tr-TR +ts-ZA +tt-RU +twq-NE +tzm-Arab-MA +tzm-Latn-DZ +tzm-Latn-MA +tzm-Tfng-MA +ug-CN +uk-UA +ur-IN +ur-PK +uz-Arab-AF +uz-Cyrl-UZ +uz-Latn-UZ +vai-Latn-LR +vai-Vaii-LR +ve-ZA +vi-VN +vo-001 +vun-TZ +wae-CH +wal-ET +wo-SN +xh-ZA +xog-UG +yav-CM +yi-001 +yo-BJ +yo-NG +zgh-Tfng-MA +zh-CN +zh-Hans-HK +zh-Hans-MO +zh-HK +zh-MO +zh-SG +zh-TW +zu-ZA \ No newline at end of file diff --git a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj index 381ebe1f1..cda6aee4c 100644 --- a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj +++ b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj @@ -4,6 +4,14 @@ Cuemon + + + + + + + + diff --git a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs index 1dc476c38..381ea6900 100644 --- a/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Reflection/AssemblyDecoratorExtensionsTest.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using System.Linq; +using Cuemon.Assets; using Cuemon.Extensions.Xunit; using Xunit; using Xunit.Abstractions; @@ -75,8 +76,8 @@ public void GetProductVersion_ShouldReturnProductVersion() [Fact] public void GetManifestResources_ShouldRetrieveCultureInfoSpecificCultures() { - var a = typeof(Disposable).Assembly; - var erbn = Decorator.Enclose(a).GetManifestResources($"{nameof(Cuemon)}.{nameof(Globalization)}.CultureInfo.SpecificCultures.dsv"); + var a = typeof(ClassBase).Assembly; + var erbn = Decorator.Enclose(a).GetManifestResources($"{nameof(Cuemon)}.{nameof(Assets)}.CultureInfo.SpecificCultures.dsv"); var erbnv = erbn.Single().Value; var erbce = Decorator.Enclose(a).GetManifestResources(".d", ManifestResourceMatch.ContainsExtension); var erbcev = erbn.Single().Value; From 06e443206384690845e688529dd7422efc0e870c Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 19:16:12 +0200 Subject: [PATCH 176/385] Diagnosticated using Docker; Linux is terrible compared to Windows in regards to handling many threads (at least from dotnetcore). Including condition check on OS and reduced pressure on Linux test env. --- .../ParallelFactoryAsyncTest.cs | 16 +++++++++------- .../ParallelFactoryTest.cs | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs index 6ef4f59d1..137dbba0b 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Cuemon.Extensions.Xunit; @@ -13,7 +14,8 @@ namespace Cuemon.Threading public class ParallelFactoryAsyncTest : Test { private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(2)); - private readonly int _extremePartitionSize = 2048; + private readonly int _extremePartitionSize = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 512 : 4096; + private readonly int _longRunningTaskInMs = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 100 : 1000; public ParallelFactoryAsyncTest(ITestOutputHelper output) : base(output) { @@ -72,7 +74,7 @@ public async Task ForAsync_ShouldRunConcurrent_LongRunning_SystemPartition() await ParallelFactory.ForAsync(0, count, async (i, ct) => { - await Task.Delay(1000, ct); + await Task.Delay(_longRunningTaskInMs, ct); cb.Add(i); }, o => o.CancellationToken = _cts.Token); @@ -150,7 +152,7 @@ public async Task ForResultAsync_ShouldRunConcurrent_LongRunning_SystemPartition var result = await ParallelFactory.ForResultAsync(0, count, async (i, ct) => { - await Task.Delay(1000, ct); + await Task.Delay(_longRunningTaskInMs, ct); cb.Add(i); return i; }, o => o.CancellationToken = _cts.Token); @@ -234,7 +236,7 @@ public async Task ForEachAsync_ShouldRunConcurrent_LongRunning_SystemPartition() await ParallelFactory.ForEachAsync(ic, async (i, ct) => { - await Task.Delay(1000, ct); + await Task.Delay(_longRunningTaskInMs, ct); cb.Add(i); }, o => o.CancellationToken = _cts.Token); @@ -319,7 +321,7 @@ public async Task ForEachResultAsync_ShouldRunConcurrent_LongRunning_SystemParti var result = await ParallelFactory.ForEachResultAsync(ic, async (i, ct) => { - await Task.Delay(1000, ct); + await Task.Delay(_longRunningTaskInMs, ct); cb.Add(i); return i; }, o => o.CancellationToken = _cts.Token); @@ -407,7 +409,7 @@ public async Task WhileAsync_ShouldRunConcurrent_LongRunning_SystemPartition() await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => { - await Task.Delay(1000, ct); + await Task.Delay(_longRunningTaskInMs, ct); cb.Add(i); }, o => o.CancellationToken = _cts.Token); @@ -496,7 +498,7 @@ public async Task WhileResultAsync_ShouldRunConcurrent_LongRunning_SystemPartiti var result = await AdvancedParallelFactory.WhileResultAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => { - await Task.Delay(1000, ct); + await Task.Delay(_longRunningTaskInMs, ct); cb.Add(i); return i; }, o => o.CancellationToken = _cts.Token); diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs index 01320ae0c..03148d212 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Cuemon.Extensions.Xunit; @@ -13,7 +14,8 @@ namespace Cuemon.Threading public class ParallelFactoryTest : Test { private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(2)); - private readonly int _extremePartitionSize = 2048; + private readonly int _extremePartitionSize = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 512 : 4096; + private readonly int _longRunningTaskInMs = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 100 : 1000; public ParallelFactoryTest(ITestOutputHelper output) : base(output) { @@ -87,7 +89,7 @@ public void For_ShouldRunConcurrent_LongRunning_SystemPartition() ParallelFactory.For(0, count, i => { - Thread.Sleep(1000); + Thread.Sleep(_longRunningTaskInMs); cb.Add(i); }, o => o.CancellationToken = _cts.Token); @@ -184,7 +186,7 @@ public void ForResult_ShouldRunConcurrent_LongRunning_SystemPartition() var result = ParallelFactory.ForResult(0, count, i => { - Thread.Sleep(1000); + Thread.Sleep(_longRunningTaskInMs); cb.Add(i); return i; }, o => o.CancellationToken = _cts.Token); @@ -283,7 +285,7 @@ public void ForEach_ShouldRunConcurrent_LongRunning_SystemPartition() ParallelFactory.ForEach(ic, i => { - Thread.Sleep(1000); + Thread.Sleep(_longRunningTaskInMs); cb.Add(i); }, o => o.CancellationToken = _cts.Token); @@ -385,7 +387,7 @@ public void ForEachResult_ShouldRunConcurrent_LongRunning_SystemPartition() var result = ParallelFactory.ForEachResult(ic, i => { - Thread.Sleep(1000); + Thread.Sleep(_longRunningTaskInMs); cb.Add(i); return i; }, o => o.CancellationToken = _cts.Token); @@ -488,7 +490,7 @@ public void While_ShouldRunConcurrent_LongRunning_SystemPartition() AdvancedParallelFactory.While(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => { - Thread.Sleep(1000); + Thread.Sleep(_longRunningTaskInMs); cb.Add(i); }); @@ -592,7 +594,7 @@ public void WhileResult_ShouldRunConcurrent_LongRunning_SystemPartition() var result = AdvancedParallelFactory.WhileResult(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => { - Thread.Sleep(1000); + Thread.Sleep(_longRunningTaskInMs); cb.Add(i); return i; }, o => o.CancellationToken = _cts.Token); From 47a496f51ca2ff9641dfb7c68e556c0c8da44fed Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 22:07:55 +0200 Subject: [PATCH 177/385] Removed potential cause of deadlock on Linux (unit test). --- .../ParallelFactoryTest.cs | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs index 03148d212..e79765618 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs @@ -70,14 +70,6 @@ public void For_ShouldRunConcurrent_IgniteCancellation() TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - - var remaining = 1; - while (remaining > 0) // exhaust remaining threads - { - var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); - remaining = differenceBecauseOfBackgroundCancellation.Count; - } - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] @@ -168,14 +160,6 @@ public void ForResult_ShouldRunConcurrent_IgniteCancellation() TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - - var remaining = 1; - while (remaining > 0) // exhaust remaining threads - { - var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); - remaining = differenceBecauseOfBackgroundCancellation.Count; - } - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] @@ -266,14 +250,6 @@ public void ForEach_ShouldRunConcurrent_IgniteCancellation() TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - - var remaining = 1; - while (remaining > 0) // exhaust remaining threads - { - var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); - remaining = differenceBecauseOfBackgroundCancellation.Count; - } - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] @@ -362,20 +338,10 @@ public void ForEachResult_ShouldRunConcurrent_IgniteCancellation() Assert.IsAssignableFrom(ae.InnerExceptions.FirstOrDefault(ex => ex.GetType().IsAssignableFrom(typeof(TaskCanceledException)))); - Thread.Sleep(500); // wait for possible background threads being canceled - TestOutput.WriteLine(x.ToString()); TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - - var remaining = 1; - while (remaining > 0) // exhaust remaining threads - { - var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); - remaining = differenceBecauseOfBackgroundCancellation.Count; - } - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] @@ -470,14 +436,6 @@ public void While_ShouldRunConcurrent_IgniteCancellation() TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - - var remaining = 1; - while (remaining > 0) // exhaust remaining threads - { - var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); - remaining = differenceBecauseOfBackgroundCancellation.Count; - } - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] @@ -574,14 +532,6 @@ public void WhileResult_ShouldRunConcurrent_IgniteCancellation() TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - - var remaining = 1; - while (remaining > 0) // exhaust remaining threads - { - var differenceBecauseOfBackgroundCancellation = cb.OrderBy(i => i).Except(Generate.RangeOf(cb.Count, i => i)).ToList(); - remaining = differenceBecauseOfBackgroundCancellation.Count; - } - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] From a90a8f4902caca2a4866c1ae44a5e309b6609ed0 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 11 Sep 2020 22:44:13 +0200 Subject: [PATCH 178/385] Changed to TimeSpan (note to self: practise what you teach!) --- .../ParallelFactoryAsyncTest.cs | 67 ++++++++++++------- .../ParallelFactoryTest.cs | 67 ++++++++++++------- 2 files changed, 84 insertions(+), 50 deletions(-) diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs index 137dbba0b..c5fd109d5 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs @@ -13,9 +13,9 @@ namespace Cuemon.Threading { public class ParallelFactoryAsyncTest : Test { - private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(2)); + private readonly TimeSpan _maxAllowedTestTime = TimeSpan.FromMinutes(1); + private readonly TimeSpan _longRunningTaskWaitTime = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? TimeSpan.FromMilliseconds(1) : TimeSpan.FromMilliseconds(10); private readonly int _extremePartitionSize = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 512 : 4096; - private readonly int _longRunningTaskInMs = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 100 : 1000; public ParallelFactoryAsyncTest(ITestOutputHelper output) : base(output) { @@ -24,6 +24,7 @@ public ParallelFactoryAsyncTest(ITestOutputHelper output) : base(output) [Fact] public async Task ForAsync_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var expected = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); @@ -34,7 +35,7 @@ await ParallelFactory.ForAsync(0, count, async (i, ct) => cb.Add(i); }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = 64; }); @@ -68,15 +69,16 @@ await ParallelFactory.ForAsync(0, count, async (i, ct) => [Fact] public async Task ForAsync_ShouldRunConcurrent_LongRunning_SystemPartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = sbyte.MaxValue; var expected = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); await ParallelFactory.ForAsync(0, count, async (i, ct) => { - await Task.Delay(_longRunningTaskInMs, ct); + await Task.Delay(_longRunningTaskWaitTime, ct); cb.Add(i); - }, o => o.CancellationToken = _cts.Token); + }, o => o.CancellationToken = cts.Token); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -102,6 +104,7 @@ await ParallelFactory.ForAsync(0, count, async (i, ct) => [Fact] public async Task ForResultAsync_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var cb = new ConcurrentBag(); @@ -112,7 +115,7 @@ public async Task ForResultAsync_ShouldRunConcurrent() return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = 64; }); @@ -147,15 +150,16 @@ await ParallelFactory.ForResultAsync(0, count, async (i, ct) => [Fact] public async Task ForResultAsync_ShouldRunConcurrent_LongRunning_SystemPartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = sbyte.MaxValue; var cb = new ConcurrentBag(); var result = await ParallelFactory.ForResultAsync(0, count, async (i, ct) => { - await Task.Delay(_longRunningTaskInMs, ct); + await Task.Delay(_longRunningTaskWaitTime, ct); cb.Add(i); return i; - }, o => o.CancellationToken = _cts.Token); + }, o => o.CancellationToken = cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -164,6 +168,7 @@ public async Task ForResultAsync_ShouldRunConcurrent_LongRunning_SystemPartition [Fact] public async Task ForResultAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = short.MaxValue; var cb = new ConcurrentBag(); @@ -174,7 +179,7 @@ public async Task ForResultAsync_ShouldRunConcurrent_LongRunning_ExtremePartitio return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = _extremePartitionSize; }); @@ -185,6 +190,7 @@ public async Task ForResultAsync_ShouldRunConcurrent_LongRunning_ExtremePartitio [Fact] public async Task ForEachAsync_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); @@ -195,7 +201,7 @@ await ParallelFactory.ForEachAsync(ic, async (i, ct) => cb.Add(i); }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = 64; }); @@ -230,15 +236,16 @@ await ParallelFactory.ForEachAsync(ic, async (i, ct) => [Fact] public async Task ForEachAsync_ShouldRunConcurrent_LongRunning_SystemPartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = sbyte.MaxValue; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); await ParallelFactory.ForEachAsync(ic, async (i, ct) => { - await Task.Delay(_longRunningTaskInMs, ct); + await Task.Delay(_longRunningTaskWaitTime, ct); cb.Add(i); - }, o => o.CancellationToken = _cts.Token); + }, o => o.CancellationToken = cts.Token); Assert.Equal(count, cb.Count); Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); @@ -247,6 +254,7 @@ await ParallelFactory.ForEachAsync(ic, async (i, ct) => [Fact] public async Task ForEachAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = short.MaxValue; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); @@ -257,7 +265,7 @@ await ParallelFactory.ForEachAsync(ic, async (i, ct) => cb.Add(i); }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = _extremePartitionSize; }); @@ -268,6 +276,7 @@ await ParallelFactory.ForEachAsync(ic, async (i, ct) => [Fact] public async Task ForEachResultAsync_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); @@ -279,7 +288,7 @@ public async Task ForEachResultAsync_ShouldRunConcurrent() return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = 64; }); @@ -315,16 +324,17 @@ await ParallelFactory.ForEachResultAsync(ic, async (i, ct) => [Fact] public async Task ForEachResultAsync_ShouldRunConcurrent_LongRunning_SystemPartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = sbyte.MaxValue; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); var result = await ParallelFactory.ForEachResultAsync(ic, async (i, ct) => { - await Task.Delay(_longRunningTaskInMs, ct); + await Task.Delay(_longRunningTaskWaitTime, ct); cb.Add(i); return i; - }, o => o.CancellationToken = _cts.Token); + }, o => o.CancellationToken = cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -333,6 +343,7 @@ public async Task ForEachResultAsync_ShouldRunConcurrent_LongRunning_SystemParti [Fact] public async Task ForEachResultAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = short.MaxValue; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); @@ -344,7 +355,7 @@ public async Task ForEachResultAsync_ShouldRunConcurrent_LongRunning_ExtremePart return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = _extremePartitionSize; }); @@ -355,6 +366,7 @@ public async Task ForEachResultAsync_ShouldRunConcurrent_LongRunning_ExtremePart [Fact] public async Task WhileAsync_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var expected = Generate.RangeOf(count, i => i); var ic = new Queue(expected); @@ -366,7 +378,7 @@ await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(ou cb.Add(i); }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = 64; }); @@ -402,6 +414,7 @@ await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(ou [Fact] public async Task WhileAsync_ShouldRunConcurrent_LongRunning_SystemPartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = sbyte.MaxValue; var expected = Generate.RangeOf(count, i => i); var ic = new Queue(expected); @@ -409,9 +422,9 @@ public async Task WhileAsync_ShouldRunConcurrent_LongRunning_SystemPartition() await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => { - await Task.Delay(_longRunningTaskInMs, ct); + await Task.Delay(_longRunningTaskWaitTime, ct); cb.Add(i); - }, o => o.CancellationToken = _cts.Token); + }, o => o.CancellationToken = cts.Token); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -420,6 +433,7 @@ await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(ou [Fact] public async Task WhileAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = short.MaxValue; var expected = Generate.RangeOf(count, i => i); var ic = new Queue(expected); @@ -431,7 +445,7 @@ await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(ou cb.Add(i); }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = _extremePartitionSize; }); @@ -442,6 +456,7 @@ await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(ou [Fact] public async Task WhileResultAsync_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var expected = Generate.RangeOf(count, i => i); var ic = new Queue(expected); @@ -454,7 +469,7 @@ public async Task WhileResultAsync_ShouldRunConcurrent() return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = 64; }); @@ -491,6 +506,7 @@ await AdvancedParallelFactory.WhileResultAsync(ic, () => Task.FromResult(ic.TryP [Fact] public async Task WhileResultAsync_ShouldRunConcurrent_LongRunning_SystemPartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = sbyte.MaxValue; var expected = Generate.RangeOf(count, i => i); var ic = new Queue(expected); @@ -498,10 +514,10 @@ public async Task WhileResultAsync_ShouldRunConcurrent_LongRunning_SystemPartiti var result = await AdvancedParallelFactory.WhileResultAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => { - await Task.Delay(_longRunningTaskInMs, ct); + await Task.Delay(_longRunningTaskWaitTime, ct); cb.Add(i); return i; - }, o => o.CancellationToken = _cts.Token); + }, o => o.CancellationToken = cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -510,6 +526,7 @@ public async Task WhileResultAsync_ShouldRunConcurrent_LongRunning_SystemPartiti [Fact] public async Task WhileResultAsync_ShouldRunConcurrent_LongRunning_ExtremePartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = short.MaxValue; var expected = Generate.RangeOf(count, i => i); var ic = new Queue(expected); @@ -522,7 +539,7 @@ public async Task WhileResultAsync_ShouldRunConcurrent_LongRunning_ExtremePartit return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = _extremePartitionSize; }); diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs index e79765618..b49290f3c 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs @@ -13,9 +13,9 @@ namespace Cuemon.Threading { public class ParallelFactoryTest : Test { - private readonly CancellationTokenSource _cts = new CancellationTokenSource(TimeSpan.FromMinutes(2)); + private readonly TimeSpan _maxAllowedTestTime = TimeSpan.FromMinutes(1); + private readonly TimeSpan _longRunningTaskWaitTime = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? TimeSpan.FromMilliseconds(1) : TimeSpan.FromMilliseconds(10); private readonly int _extremePartitionSize = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 512 : 4096; - private readonly int _longRunningTaskInMs = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 100 : 1000; public ParallelFactoryTest(ITestOutputHelper output) : base(output) { @@ -24,6 +24,7 @@ public ParallelFactoryTest(ITestOutputHelper output) : base(output) [Fact] public void For_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var expected = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); @@ -34,7 +35,7 @@ public void For_ShouldRunConcurrent() cb.Add(i); }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.CreationOptions = TaskCreationOptions.None; }); @@ -75,15 +76,16 @@ public void For_ShouldRunConcurrent_IgniteCancellation() [Fact] public void For_ShouldRunConcurrent_LongRunning_SystemPartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = sbyte.MaxValue; var expected = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); ParallelFactory.For(0, count, i => { - Thread.Sleep(_longRunningTaskInMs); + Thread.Sleep(_longRunningTaskWaitTime); cb.Add(i); - }, o => o.CancellationToken = _cts.Token); + }, o => o.CancellationToken = cts.Token); Assert.Equal(count, cb.Count); Assert.True(expected.SequenceEqual(cb.OrderBy(i => i)), "expected.SequenceEqual(cb.OrderBy(i => i))"); @@ -92,6 +94,7 @@ public void For_ShouldRunConcurrent_LongRunning_SystemPartition() [Fact] public void For_ShouldRunConcurrent_LongRunning_ExtremePartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = short.MaxValue; var expected = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); @@ -102,7 +105,7 @@ public void For_ShouldRunConcurrent_LongRunning_ExtremePartition() cb.Add(i); }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = _extremePartitionSize; }); @@ -113,6 +116,7 @@ public void For_ShouldRunConcurrent_LongRunning_ExtremePartition() [Fact] public void ForResult_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var cb = new ConcurrentBag(); @@ -123,7 +127,7 @@ public void ForResult_ShouldRunConcurrent() return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.CreationOptions = TaskCreationOptions.None; }); @@ -165,15 +169,16 @@ public void ForResult_ShouldRunConcurrent_IgniteCancellation() [Fact] public void ForResult_ShouldRunConcurrent_LongRunning_SystemPartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = sbyte.MaxValue; var cb = new ConcurrentBag(); var result = ParallelFactory.ForResult(0, count, i => { - Thread.Sleep(_longRunningTaskInMs); + Thread.Sleep(_longRunningTaskWaitTime); cb.Add(i); return i; - }, o => o.CancellationToken = _cts.Token); + }, o => o.CancellationToken = cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -182,6 +187,7 @@ public void ForResult_ShouldRunConcurrent_LongRunning_SystemPartition() [Fact] public void ForResult_ShouldRunConcurrent_LongRunning_ExtremePartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = short.MaxValue; var cb = new ConcurrentBag(); @@ -192,7 +198,7 @@ public void ForResult_ShouldRunConcurrent_LongRunning_ExtremePartition() return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = _extremePartitionSize; }); @@ -203,6 +209,7 @@ public void ForResult_ShouldRunConcurrent_LongRunning_ExtremePartition() [Fact] public void ForEach_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); @@ -213,7 +220,7 @@ public void ForEach_ShouldRunConcurrent() cb.Add(i); }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.CreationOptions = TaskCreationOptions.None; }); @@ -255,15 +262,16 @@ public void ForEach_ShouldRunConcurrent_IgniteCancellation() [Fact] public void ForEach_ShouldRunConcurrent_LongRunning_SystemPartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = sbyte.MaxValue; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); ParallelFactory.ForEach(ic, i => { - Thread.Sleep(_longRunningTaskInMs); + Thread.Sleep(_longRunningTaskWaitTime); cb.Add(i); - }, o => o.CancellationToken = _cts.Token); + }, o => o.CancellationToken = cts.Token); Assert.Equal(count, cb.Count); Assert.True(ic.SequenceEqual(cb.OrderBy(i => i)), "ic.SequenceEqual(cb.OrderBy(i => i))"); @@ -272,6 +280,7 @@ public void ForEach_ShouldRunConcurrent_LongRunning_SystemPartition() [Fact] public void ForEach_ShouldRunConcurrent_LongRunning_ExtremePartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = short.MaxValue; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); @@ -282,7 +291,7 @@ public void ForEach_ShouldRunConcurrent_LongRunning_ExtremePartition() cb.Add(i); }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = _extremePartitionSize; }); @@ -293,6 +302,7 @@ public void ForEach_ShouldRunConcurrent_LongRunning_ExtremePartition() [Fact] public void ForEachResult_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); @@ -304,7 +314,7 @@ public void ForEachResult_ShouldRunConcurrent() return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.CreationOptions = TaskCreationOptions.None; }); @@ -347,16 +357,17 @@ public void ForEachResult_ShouldRunConcurrent_IgniteCancellation() [Fact] public void ForEachResult_ShouldRunConcurrent_LongRunning_SystemPartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = sbyte.MaxValue; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); var result = ParallelFactory.ForEachResult(ic, i => { - Thread.Sleep(_longRunningTaskInMs); + Thread.Sleep(_longRunningTaskWaitTime); cb.Add(i); return i; - }, o => o.CancellationToken = _cts.Token); + }, o => o.CancellationToken = cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -365,6 +376,7 @@ public void ForEachResult_ShouldRunConcurrent_LongRunning_SystemPartition() [Fact] public void ForEachResult_ShouldRunConcurrent_LongRunning_ExtremePartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = short.MaxValue; var ic = Generate.RangeOf(count, i => i); var cb = new ConcurrentBag(); @@ -376,7 +388,7 @@ public void ForEachResult_ShouldRunConcurrent_LongRunning_ExtremePartition() return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = _extremePartitionSize; }); @@ -387,6 +399,7 @@ public void ForEachResult_ShouldRunConcurrent_LongRunning_ExtremePartition() [Fact] public void While_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var expected = Generate.RangeOf(count, i => i); var ic = new Queue(expected); @@ -398,7 +411,7 @@ public void While_ShouldRunConcurrent() cb.Add(i); }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.CreationOptions = TaskCreationOptions.None; }); @@ -448,7 +461,7 @@ public void While_ShouldRunConcurrent_LongRunning_SystemPartition() AdvancedParallelFactory.While(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => { - Thread.Sleep(_longRunningTaskInMs); + Thread.Sleep(_longRunningTaskWaitTime); cb.Add(i); }); @@ -459,6 +472,7 @@ public void While_ShouldRunConcurrent_LongRunning_SystemPartition() [Fact] public void While_ShouldRunConcurrent_LongRunning_ExtremePartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = short.MaxValue; var expected = Generate.RangeOf(count, i => i); var ic = new Queue(expected); @@ -470,7 +484,7 @@ public void While_ShouldRunConcurrent_LongRunning_ExtremePartition() cb.Add(i); }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = _extremePartitionSize; }); @@ -481,6 +495,7 @@ public void While_ShouldRunConcurrent_LongRunning_ExtremePartition() [Fact] public void WhileResult_ShouldRunConcurrent() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = 1000; var expected = Generate.RangeOf(count, i => i); var ic = new Queue(expected); @@ -493,7 +508,7 @@ public void WhileResult_ShouldRunConcurrent() return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.CreationOptions = TaskCreationOptions.None; }); @@ -537,6 +552,7 @@ public void WhileResult_ShouldRunConcurrent_IgniteCancellation() [Fact] public void WhileResult_ShouldRunConcurrent_LongRunning_SystemPartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = sbyte.MaxValue; var expected = Generate.RangeOf(count, i => i); var ic = new Queue(expected); @@ -544,10 +560,10 @@ public void WhileResult_ShouldRunConcurrent_LongRunning_SystemPartition() var result = AdvancedParallelFactory.WhileResult(ic, () => ic.TryPeek(out _), intProvider => intProvider.Dequeue(), i => { - Thread.Sleep(_longRunningTaskInMs); + Thread.Sleep(_longRunningTaskWaitTime); cb.Add(i); return i; - }, o => o.CancellationToken = _cts.Token); + }, o => o.CancellationToken = cts.Token); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); @@ -556,6 +572,7 @@ public void WhileResult_ShouldRunConcurrent_LongRunning_SystemPartition() [Fact] public void WhileResult_ShouldRunConcurrent_LongRunning_ExtremePartition() { + var cts = new CancellationTokenSource(_maxAllowedTestTime); var count = short.MaxValue; var expected = Generate.RangeOf(count, i => i); var ic = new Queue(expected); @@ -568,7 +585,7 @@ public void WhileResult_ShouldRunConcurrent_LongRunning_ExtremePartition() return i; }, o => { - o.CancellationToken = _cts.Token; + o.CancellationToken = cts.Token; o.PartitionSize = _extremePartitionSize; }); From 291ddabd26fccf66c63736bf36286ded5ea2d7da Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 14 Sep 2020 00:11:34 +0200 Subject: [PATCH 179/385] Added initial DocFx build using Docker. --- BuildDocfxImage.ps1 | 4 ++ Dockerfile.docfx | 47 +++++++++++++ docfx/docfx.json | 158 ++++++++++++++++++++------------------------ 3 files changed, 122 insertions(+), 87 deletions(-) create mode 100644 BuildDocfxImage.ps1 create mode 100644 Dockerfile.docfx diff --git a/BuildDocfxImage.ps1 b/BuildDocfxImage.ps1 new file mode 100644 index 000000000..696cac558 --- /dev/null +++ b/BuildDocfxImage.ps1 @@ -0,0 +1,4 @@ +docfx metadata docfx/docfx.json +docker build -t cuemon-docfx:6.0.0 -f Dockerfile.docfx . +remove-item docfx/obj -recurse +get-childItem -recurse -path docfx/api -include *.yml, .manifest | remove-item \ No newline at end of file diff --git a/Dockerfile.docfx b/Dockerfile.docfx new file mode 100644 index 000000000..b48d35686 --- /dev/null +++ b/Dockerfile.docfx @@ -0,0 +1,47 @@ +# escape=` + +FROM nginx:1.19.2 AS base +RUN rm -rf /usr/share/nginx/html/* + +FROM mono:6.10.0.104 AS build +ARG DOCFX_VERSION=v2.56.2 + +ENV PATH ${PATH}:/opt/docfx +ENV DOCFX_SOURCE_BRANCH_NAME="development" + +# MONO IMAGE +RUN curl -sSL --output packages-microsoft-prod.deb https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb +RUN dpkg -i packages-microsoft-prod.deb +RUN apt-get update ` +&& apt-get install -y powershell zip apt-transport-https dirmngr gnupg ca-certificates git ` +&& mkdir -p /opt/docfx/ ` +&& curl -sSL --output docfx.zip "https://github.com/dotnet/docfx/releases/download/${DOCFX_VERSION}/docfx.zip" ` +&& unzip docfx.zip -d /opt/docfx/ ` +&& rm docfx.zip ` +&& echo '#!/bin/bash' >> /opt/docfx/docfx ` +&& echo 'exec mono /opt/docfx/docfx.exe $@' >> /opt/docfx/docfx ` +&& chmod +x /opt/docfx/docfx ` +&& apt-get -y purge unzip + +SHELL ["/opt/microsoft/powershell/7/pwsh", "-Command"] + +WORKDIR /build + +ADD ["docfx", "docfx"] +#ADD ["src", "src"] INCLUDE THIS WHEN DOCFX IS MATURED - SEE NOTE BELOW + +RUN cd docfx; ` +# docfx metadata; ` INCLUDE THIS WHEN DOCFX IS MATURED - SEE NOTE BELOW +docfx build + +# After carefull considerations, I have concluded that DoxFx is not a matured product to be run entirely as part of a Docker image. +# Reason is that for DocFx to resolve cref correctly (and assembly names) it requires the msbuild project file (csproj) and not only the source files (cs). +# As stated by docfx team themself: Visual Studio 2019 is needed for docfx metadata msbuild projects. +# Given that VS2019 is to hard a dependency, I have moved the docfx metadata taskto my local development environment. +# It would have been great to have all documentation done in a container - but for now this is not an option without significant drawbacks. + +FROM base AS final +WORKDIR /usr/share/nginx/html +COPY --from=build /build/docfx/wwwroot /usr/share/nginx/html + +ENTRYPOINT ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/docfx/docfx.json b/docfx/docfx.json index 0d4a83a71..b0dc987fa 100644 --- a/docfx/docfx.json +++ b/docfx/docfx.json @@ -4,7 +4,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Core/**.cs*" + // "Cuemon.Core/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -23,7 +23,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Data/**.cs*" + // "Cuemon.Data/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -42,7 +42,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Data.Integrity/**.cs*" + // "Cuemon.Data.Integrity/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -61,7 +61,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Integrity/**.cs*" + // "Cuemon.Integrity/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -80,7 +80,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Data.SqlClient/**.cs*" + // "Cuemon.Data.SqlClient/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -99,7 +99,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Diagnostics/**.cs*" + // "Cuemon.Diagnostics/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -118,7 +118,7 @@ // "src": [ // { // "files": [ - // "Cuemon.IO/**.cs*" + // "Cuemon.IO/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -137,7 +137,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Net/**.cs*" + // "Cuemon.Net/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -156,7 +156,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Resilience/**.cs*" + // "Cuemon.Resilience/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -175,7 +175,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Runtime.Caching/**.cs*" + // "Cuemon.Runtime.Caching/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -194,7 +194,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Security.Cryptography/**.cs*" + // "Cuemon.Security.Cryptography/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -213,7 +213,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Threading/**.cs*" + // "Cuemon.Threading/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -232,7 +232,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Xml/**.cs*" + // "Cuemon.Xml/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -251,7 +251,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Collections.Generic/**.cs*" + // "Cuemon.Extensions.Collections.Generic/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -270,7 +270,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Collections.Specialized/**.cs*" + // "Cuemon.Extensions.Collections.Specialized/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -289,7 +289,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Core/**.cs*" + // "Cuemon.Extensions.Core/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -308,7 +308,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Data/**.cs*" + // "Cuemon.Extensions.Data/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -327,7 +327,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Data.Integrity/**.cs*" + // "Cuemon.Extensions.Data.Integrity/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -346,7 +346,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.DependencyInjection/**.cs*" + // "Cuemon.Extensions.DependencyInjection/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -365,7 +365,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Diagnostics/**.cs*" + // "Cuemon.Extensions.Diagnostics/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -384,7 +384,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.IO/**.cs*" + // "Cuemon.Extensions.IO/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -403,7 +403,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Net/**.cs*" + // "Cuemon.Extensions.Net/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -422,7 +422,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Newtonsoft.Json/**.cs*" + // "Cuemon.Extensions.Newtonsoft.Json/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -441,7 +441,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Reflection/**.cs*" + // "Cuemon.Extensions.Reflection/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -460,7 +460,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Text/**.cs*" + // "Cuemon.Extensions.Text/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -479,7 +479,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Threading/**.cs*" + // "Cuemon.Extensions.Threading/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -498,7 +498,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Xml/**.cs*" + // "Cuemon.Extensions.Xml/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -517,7 +517,7 @@ // "src": [ // { // "files": [ - // "Cuemon.AspNetCore/**.cs*" + // "Cuemon.AspNetCore/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -536,7 +536,7 @@ // "src": [ // { // "files": [ - // "Cuemon.AspNetCore.Authentication/**.cs*" + // "Cuemon.AspNetCore.Authentication/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -555,7 +555,7 @@ // "src": [ // { // "files": [ - // "Cuemon.AspNetCore.Mvc/**.cs*" + // "Cuemon.AspNetCore.Mvc/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -574,7 +574,7 @@ // "src": [ // { // "files": [ - // "Cuemon.AspNetCore.Razor/**.cs*" + // "Cuemon.AspNetCore.Razor/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -593,7 +593,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.AspNetCore/**.cs*" + // "Cuemon.Extensions.AspNetCore/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -612,7 +612,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.AspNetCore.Mvc/**.cs*" + // "Cuemon.Extensions.AspNetCore.Mvc/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -631,7 +631,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/**.cs*" + // "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -650,7 +650,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/**.cs*" + // "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -669,7 +669,7 @@ // "src": [ // { // "files": [ - // "Cuemon.Extensions.Xunit/**.cs*" + // "Cuemon.Extensions.Xunit/**.csproj" // ], // "exclude": [ // "**/bin/**", @@ -690,22 +690,18 @@ "src": [ { "files": [ - "Cuemon.Core/**.cs*", - "Cuemon.Data/**.cs*", - "Cuemon.Data.Integrity/**.cs*", - "Cuemon.Data.SqlClient/**.cs*", - "Cuemon.Diagnostics/**.cs*", - "Cuemon.IO/**.cs*", - "Cuemon.Net/**.cs*", - "Cuemon.Resilience/**.cs*", - "Cuemon.Runtime.Caching/**.cs*", - "Cuemon.Security.Cryptography/**.cs*", - "Cuemon.Threading/**.cs*", - "Cuemon.Xml/**.cs*" - ], - "exclude": [ - "**/bin/**", - "**/obj/**" + "Cuemon.Core/**.csproj", + "Cuemon.Data/**.csproj", + "Cuemon.Data.Integrity/**.csproj", + "Cuemon.Data.SqlClient/**.csproj", + "Cuemon.Diagnostics/**.csproj", + "Cuemon.IO/**.csproj", + "Cuemon.Net/**.csproj", + "Cuemon.Resilience/**.csproj", + "Cuemon.Runtime.Caching/**.csproj", + "Cuemon.Security.Cryptography/**.csproj", + "Cuemon.Threading/**.csproj", + "Cuemon.Xml/**.csproj" ], "src": "../src" } @@ -720,25 +716,21 @@ "src": [ { "files": [ - "Cuemon.Extensions.Collections.Generic/**.cs*", - "Cuemon.Extensions.Collections.Specialized/**.cs*", - "Cuemon.Extensions.Core/**.cs*", - "Cuemon.Extensions.Data/**.cs*", - "Cuemon.Extensions.Data.Integrity/**.cs*", - "Cuemon.Extensions.DependencyInjection/**.cs*", - "Cuemon.Extensions.Diagnostics/**.cs*", - "Cuemon.Extensions.IO/**.cs*", - "Cuemon.Extensions.Net/**.cs*", - "Cuemon.Extensions.Newtonsoft.Json/**.cs*", - "Cuemon.Extensions.Reflection/**.cs*", - "Cuemon.Extensions.Text/**.cs*", - "Cuemon.Extensions.Threading/**.cs*", - "Cuemon.Extensions.Xml/**.cs*", - "Cuemon.Extensions.Xunit/**.cs*" - ], - "exclude": [ - "**/bin/**", - "**/obj/**" + "Cuemon.Extensions.Collections.Generic/**.csproj", + "Cuemon.Extensions.Collections.Specialized/**.csproj", + "Cuemon.Extensions.Core/**.csproj", + "Cuemon.Extensions.Data/**.csproj", + "Cuemon.Extensions.Data.Integrity/**.csproj", + "Cuemon.Extensions.DependencyInjection/**.csproj", + "Cuemon.Extensions.Diagnostics/**.csproj", + "Cuemon.Extensions.IO/**.csproj", + "Cuemon.Extensions.Net/**.csproj", + "Cuemon.Extensions.Newtonsoft.Json/**.csproj", + "Cuemon.Extensions.Reflection/**.csproj", + "Cuemon.Extensions.Text/**.csproj", + "Cuemon.Extensions.Threading/**.csproj", + "Cuemon.Extensions.Xml/**.csproj", + "Cuemon.Extensions.Xunit/**.csproj" ], "src": "../src" } @@ -753,14 +745,10 @@ "src": [ { "files": [ - "Cuemon.AspNetCore/**.cs*", - "Cuemon.AspNetCore.Authentication/**.cs*", - "Cuemon.AspNetCore.Mvc/**.cs*", - "Cuemon.AspNetCore.Razor/**.cs*" - ], - "exclude": [ - "**/bin/**", - "**/obj/**" + "Cuemon.AspNetCore/**.csproj", + "Cuemon.AspNetCore.Authentication/**.csproj", + "Cuemon.AspNetCore.Mvc/**.csproj", + "Cuemon.AspNetCore.Razor/**.csproj" ], "src": "../src" } @@ -775,14 +763,10 @@ "src": [ { "files": [ - "Cuemon.Extensions.AspNetCore/**.cs*", - "Cuemon.Extensions.AspNetCore.Mvc/**.cs*", - "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/**.cs*", - "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/**.cs*" - ], - "exclude": [ - "**/bin/**", - "**/obj/**" + "Cuemon.Extensions.AspNetCore/**.csproj", + "Cuemon.Extensions.AspNetCore.Mvc/**.csproj", + "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/**.csproj", + "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/**.csproj" ], "src": "../src" } From 5a94e897d42e13b203ccf6614663c136cdb29d83 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 14 Sep 2020 00:12:45 +0200 Subject: [PATCH 180/385] Changed timeout back to 75. Rason for extensive run is due to SonarCloud bug/feature. --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 285bfff66..ed603b7c4 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -10,7 +10,7 @@ variables: jobs: - job: CI - timeoutInMinutes: 120 + timeoutInMinutes: 75 strategy: matrix: From 8331b22e97c9fbfcc59ec1f1f46e82329144e3e0 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 14 Sep 2020 02:49:20 +0200 Subject: [PATCH 181/385] Changed to be compliant with Framework Design Guidelines. --- src/Cuemon.Core/TaskActionFactory.cs | 8 +++++++- src/Cuemon.Core/TaskFuncFactory.cs | 8 +++++++- src/Cuemon.Core/TemplateFactory.cs | 3 +++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Core/TaskActionFactory.cs b/src/Cuemon.Core/TaskActionFactory.cs index 6cc0fbded..b72f06ffe 100644 --- a/src/Cuemon.Core/TaskActionFactory.cs +++ b/src/Cuemon.Core/TaskActionFactory.cs @@ -448,10 +448,16 @@ internal TaskActionFactory(Func method, TTuple /// /// The token to monitor for cancellation requests. The default value is . /// A task that represents the asynchronous operation. + /// + /// No delegate was specified on the factory. + /// + /// + /// The was canceled. + /// public Task ExecuteMethodAsync(CancellationToken ct) { ThrowIfNoValidDelegate(Condition.IsNull(Method)); - if (ct.IsCancellationRequested) { throw new TaskCanceledException(); } + if (ct.IsCancellationRequested) { ct.ThrowIfCancellationRequested(); } return Method.Invoke(GenericArguments, ct); } diff --git a/src/Cuemon.Core/TaskFuncFactory.cs b/src/Cuemon.Core/TaskFuncFactory.cs index 62ef4511a..762d508a0 100644 --- a/src/Cuemon.Core/TaskFuncFactory.cs +++ b/src/Cuemon.Core/TaskFuncFactory.cs @@ -465,10 +465,16 @@ internal TaskFuncFactory(Func> method, /// /// The token to monitor for cancellation requests. The default value is . /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate associated with this instance. + /// + /// No delegate was specified on the factory. + /// + /// + /// The was canceled. + /// public Task ExecuteMethodAsync(CancellationToken ct) { ThrowIfNoValidDelegate(Condition.IsNull(Method)); - if (ct.IsCancellationRequested) { throw new TaskCanceledException(); } + if (ct.IsCancellationRequested) { ct.ThrowIfCancellationRequested(); } return Method.Invoke(GenericArguments, ct); } diff --git a/src/Cuemon.Core/TemplateFactory.cs b/src/Cuemon.Core/TemplateFactory.cs index 7db5c9124..94504d941 100644 --- a/src/Cuemon.Core/TemplateFactory.cs +++ b/src/Cuemon.Core/TemplateFactory.cs @@ -58,6 +58,9 @@ public override string ToString() /// Validates and throws an if this instance has no valid delegate. /// /// The value of a condition that can be either true or false. + /// + /// No delegate was specified on the factory. + /// protected void ThrowIfNoValidDelegate(bool delegateIsNull) { if (!HasDelegate) { throw new InvalidOperationException(delegateIsNull ? "There is no delegate specified on the factory." : FormattableString.Invariant($"There is a delegate specified on the factory, '{Decorator.Enclose(GetType()).ToFriendlyName(o => o.FullName = true)}', but it leads to a null referenced delegate wrapper.")); } From f495b73462a30ced0729c36c050c7dd0538ddb8f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 14 Sep 2020 03:02:42 +0200 Subject: [PATCH 182/385] Consequence changes of 8331b22e. --- test/Cuemon.Core.Tests/DisposableTest.cs | 2 +- .../Cuemon.IO.Tests/StreamDecoratorExtensionsTest.cs | 2 +- .../ParallelFactoryAsyncTest.cs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/Cuemon.Core.Tests/DisposableTest.cs b/test/Cuemon.Core.Tests/DisposableTest.cs index b3643d537..45145b632 100644 --- a/test/Cuemon.Core.Tests/DisposableTest.cs +++ b/test/Cuemon.Core.Tests/DisposableTest.cs @@ -95,7 +95,7 @@ public async Task SafeInvokeAsync_ShouldAbideRuleCA2000() Assert.Null(stream); Assert.Throws(() => msRef.Length); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(5)); msRef = null; diff --git a/test/Cuemon.IO.Tests/StreamDecoratorExtensionsTest.cs b/test/Cuemon.IO.Tests/StreamDecoratorExtensionsTest.cs index 694796fdd..f2c677121 100644 --- a/test/Cuemon.IO.Tests/StreamDecoratorExtensionsTest.cs +++ b/test/Cuemon.IO.Tests/StreamDecoratorExtensionsTest.cs @@ -96,7 +96,7 @@ public async Task CompressGZipAsync_ShouldThrowTaskCanceledException() var size = 1024 * 1024; var fs = Generate.RandomString(size); var os = await Decorator.Enclose(fs).ToStreamAsync(); - await Assert.ThrowsAsync(async () => await Decorator.Enclose(os).CompressGZipAsync(o => o.CancellationToken = ctsShouldFail.Token)); + await Assert.ThrowsAsync(async () => await Decorator.Enclose(os).CompressGZipAsync(o => o.CancellationToken = ctsShouldFail.Token)); } [Fact] diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs index c5fd109d5..3eeeb75c2 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs @@ -50,7 +50,7 @@ public async Task ForAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { await ParallelFactory.ForAsync(0, count, async (i, ct) => { @@ -130,7 +130,7 @@ public async Task ForResultAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { await ParallelFactory.ForResultAsync(0, count, async (i, ct) => { @@ -217,7 +217,7 @@ public async Task ForEachAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { await ParallelFactory.ForEachAsync(ic, async (i, ct) => { @@ -304,7 +304,7 @@ public async Task ForEachResultAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { await ParallelFactory.ForEachResultAsync(ic, async (i, ct) => { @@ -395,7 +395,7 @@ public async Task WhileAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => { @@ -486,7 +486,7 @@ public async Task WhileResultAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { await AdvancedParallelFactory.WhileResultAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => { From 16c32d7c5e1a91cd80b1c261456f9e299a21d7c5 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 14 Sep 2020 03:03:40 +0200 Subject: [PATCH 183/385] Updated documentation for DocFx and NuGet description. --- docfx/api/namespaces/Cuemon.Security.Cryptography.md | 5 ++++- docfx/api/namespaces/Cuemon.Threading.md | 5 +++-- docfx/api/namespaces/Cuemon.Xml.md | 5 ++++- .../Cuemon.Security.Cryptography.csproj | 4 ++-- src/Cuemon.Threading/Cuemon.Threading.csproj | 2 +- src/Cuemon.Xml/Cuemon.Xml.csproj | 2 +- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/docfx/api/namespaces/Cuemon.Security.Cryptography.md b/docfx/api/namespaces/Cuemon.Security.Cryptography.md index ef2e5f167..404595c15 100644 --- a/docfx/api/namespaces/Cuemon.Security.Cryptography.md +++ b/docfx/api/namespaces/Cuemon.Security.Cryptography.md @@ -2,4 +2,7 @@ uid: Cuemon.Security.Cryptography summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Security.Cryptography namespace contains types related to cryptographic hashing (both keyed and non-keyed) and a ready-to-use implementation of the Advanced Encryption Standard (AES) symmetric algorithm. The namespace is an addition to the System.Security.Cryptography namespace. + +Availability: NET Standard 2.0 +Complements: [System.Security.Cryptography namespace](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography?view=netstandard-2.0) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Threading.md b/docfx/api/namespaces/Cuemon.Threading.md index 1b98b62b5..02ca6dc83 100644 --- a/docfx/api/namespaces/Cuemon.Threading.md +++ b/docfx/api/namespaces/Cuemon.Threading.md @@ -2,6 +2,7 @@ uid: Cuemon.Threading summary: *content --- -The Cuemon.Threading namespace contains types that can prove helpful when working with concurrent operations. The namespace relates to the System.Threading namespace. +The Cuemon.Threading namespace contains types related to working with long-running concurrent loops and regions that utilizes both synchronous and asynchronous delegates. The namespace is an addition to the System.Threading namespace. -Availability: NET Standard 2.0 \ No newline at end of file +Availability: NET Standard 2.0 +Complements: [System.Threading namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading?view=netstandard-2.0) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.md b/docfx/api/namespaces/Cuemon.Xml.md index bb2eb3695..5e6d9057e 100644 --- a/docfx/api/namespaces/Cuemon.Xml.md +++ b/docfx/api/namespaces/Cuemon.Xml.md @@ -2,4 +2,7 @@ uid: Cuemon.Xml summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Xml namespace contains types related to encoding, converting and serialization. The included lightweight XML serializer framework offers same flexibility as the one provided by the JSON equivalent from Newtonsoft. The namespace is an addition to both System.Xml- and System.Xml.Serialization namespaces. + +Availability: NET Standard 2.0 +Complements: [System.Xml namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml?view=netstandard-2.0), [System.Xml.Serialization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization?view=netstandard-2.0) \ No newline at end of file diff --git a/src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj b/src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj index 1e3160ae1..a76121b47 100644 --- a/src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj +++ b/src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj @@ -8,8 +8,8 @@ Cuemon.Security.Cryptography Cuemon.Security.Cryptography - The Cuemon.Security.Cryptography namespace contains . - + The Cuemon.Security.Cryptography namespace contains types related to cryptographic hashing (both keyed and non-keyed) and a ready-to-use implementation of the Advanced Encryption Standard (AES) symmetric algorithm. The namespace is an addition to the System.Security.Cryptography namespace. + aes-cryptor keyed-hash-factory hmac unkeyed-hash-factory unkeyed-crypto-hash keyed-crypto-hash hmac-message-digest-5 hmac-md5 hmac-secure-hash-algorithm-1 hmac-sha1 hmac-secure-hash-algorithm-256 hmac-sha256 hmac-secure-hash-algorithm-384 hmac-sha384 hmac-secure-hash-algorithm-512 hmac-sha512 message-digest-5 md5 secure-hash-algorithm-1 sha1 secure-hash-algorithm-256 sha256 secure-hash-algorithm-384 sha384 secure-hash-algorithm-512 sha512 diff --git a/src/Cuemon.Threading/Cuemon.Threading.csproj b/src/Cuemon.Threading/Cuemon.Threading.csproj index 603e99389..95ebed6a5 100644 --- a/src/Cuemon.Threading/Cuemon.Threading.csproj +++ b/src/Cuemon.Threading/Cuemon.Threading.csproj @@ -8,7 +8,7 @@ Cuemon.Threading Cuemon.Threading - The Cuemon.Threading namespace contains types that can prove helpful when working with concurrent operations. The namespace relates to the System.Threading namespace. + The Cuemon.Threading namespace contains types related to working with long-running concurrent loops and regions that utilizes both synchronous and asynchronous delegates. The namespace is an addition to the System.Threading namespace. advanced-parallel-factory parallel-factory for-async for-each-async for-each-result-async for-result-async while-async while-result-async async-options timer-factory for-loop-ruleset diff --git a/src/Cuemon.Xml/Cuemon.Xml.csproj b/src/Cuemon.Xml/Cuemon.Xml.csproj index 0a2c58103..54e699972 100644 --- a/src/Cuemon.Xml/Cuemon.Xml.csproj +++ b/src/Cuemon.Xml/Cuemon.Xml.csproj @@ -8,7 +8,7 @@ Cuemon.Xml Cuemon.Xml - The Cuemon.Xml namespace contains features related to both the System.Xml- and System.Xml.Serialization namespaces. Included is a lightweight XML serializer framework that offers the same flexibility provided by the JSON equivalent from Newtonsoft. + The Cuemon.Xml namespace contains types related to encoding, converting and serialization. The included lightweight XML serializer framework offers same flexibility as the one provided by the JSON equivalent from Newtonsoft. The namespace is an addition to both System.Xml- and System.Xml.Serialization namespaces. xml-formatter xml-converter xml-serializer xml-factories From 3d6630ab495e4632704a7655235fb0879441758b Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 14 Sep 2020 21:22:14 +0200 Subject: [PATCH 184/385] Removed redundant check on IsCancellationRequested. --- src/Cuemon.Core/TaskActionFactory.cs | 2 +- src/Cuemon.Core/TaskFuncFactory.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Core/TaskActionFactory.cs b/src/Cuemon.Core/TaskActionFactory.cs index b72f06ffe..2de8ef77e 100644 --- a/src/Cuemon.Core/TaskActionFactory.cs +++ b/src/Cuemon.Core/TaskActionFactory.cs @@ -457,7 +457,7 @@ internal TaskActionFactory(Func method, TTuple public Task ExecuteMethodAsync(CancellationToken ct) { ThrowIfNoValidDelegate(Condition.IsNull(Method)); - if (ct.IsCancellationRequested) { ct.ThrowIfCancellationRequested(); } + ct.ThrowIfCancellationRequested(); return Method.Invoke(GenericArguments, ct); } diff --git a/src/Cuemon.Core/TaskFuncFactory.cs b/src/Cuemon.Core/TaskFuncFactory.cs index 762d508a0..f55959350 100644 --- a/src/Cuemon.Core/TaskFuncFactory.cs +++ b/src/Cuemon.Core/TaskFuncFactory.cs @@ -474,7 +474,7 @@ internal TaskFuncFactory(Func> method, public Task ExecuteMethodAsync(CancellationToken ct) { ThrowIfNoValidDelegate(Condition.IsNull(Method)); - if (ct.IsCancellationRequested) { ct.ThrowIfCancellationRequested(); } + ct.ThrowIfCancellationRequested(); return Method.Invoke(GenericArguments, ct); } From 20681181d1736532df376b605de40af75ce2a47a Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 14 Sep 2020 21:23:22 +0200 Subject: [PATCH 185/385] This is interesting; had to change it to ThrowsAnyAsync{OperationCanceledException} as Linux is throwing TaskCanceledException and Windows OperationCanceledException. --- .../ParallelFactoryAsyncTest.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs index 3eeeb75c2..7776fbb58 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryAsyncTest.cs @@ -50,7 +50,7 @@ public async Task ForAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await ParallelFactory.ForAsync(0, count, async (i, ct) => { @@ -63,7 +63,6 @@ await ParallelFactory.ForAsync(0, count, async (i, ct) => TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] @@ -130,7 +129,7 @@ public async Task ForResultAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await ParallelFactory.ForResultAsync(0, count, async (i, ct) => { @@ -144,7 +143,6 @@ await ParallelFactory.ForResultAsync(0, count, async (i, ct) => TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] @@ -217,7 +215,7 @@ public async Task ForEachAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await ParallelFactory.ForEachAsync(ic, async (i, ct) => { @@ -230,7 +228,6 @@ await ParallelFactory.ForEachAsync(ic, async (i, ct) => TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] @@ -304,7 +301,7 @@ public async Task ForEachResultAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await ParallelFactory.ForEachResultAsync(ic, async (i, ct) => { @@ -318,7 +315,6 @@ await ParallelFactory.ForEachResultAsync(ic, async (i, ct) => TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] @@ -395,7 +391,7 @@ public async Task WhileAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => { @@ -408,7 +404,6 @@ await AdvancedParallelFactory.WhileAsync(ic, () => Task.FromResult(ic.TryPeek(ou TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] @@ -486,7 +481,7 @@ public async Task WhileResultAsync_ShouldRunConcurrent_IgniteCancellation() var cb = new ConcurrentBag(); var cts = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await AdvancedParallelFactory.WhileResultAsync(ic, () => Task.FromResult(ic.TryPeek(out _)), intProvider => intProvider.Dequeue(), async (i, ct) => { @@ -500,7 +495,6 @@ await AdvancedParallelFactory.WhileResultAsync(ic, () => Task.FromResult(ic.TryP TestOutput.WriteLine($"Threads processed: {cb.Count}."); Assert.InRange(cb.Count, 200, 500); // most threads should have executed before cancellation - Assert.True(Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i)), "Generate.RangeOf(cb.Count, i => i).SequenceEqual(cb.OrderBy(i => i))"); } [Fact] From 36b4421b26a8acb025e530a4d47a237bb8ee7406 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 14 Sep 2020 22:41:14 +0200 Subject: [PATCH 186/385] Updated to retreive dynamic version and embed it to image and registry. --- BuildDocfxImage.ps1 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/BuildDocfxImage.ps1 b/BuildDocfxImage.ps1 index 696cac558..69e75910f 100644 --- a/BuildDocfxImage.ps1 +++ b/BuildDocfxImage.ps1 @@ -1,4 +1,6 @@ -docfx metadata docfx/docfx.json -docker build -t cuemon-docfx:6.0.0 -f Dockerfile.docfx . -remove-item docfx/obj -recurse -get-childItem -recurse -path docfx/api -include *.yml, .manifest | remove-item \ No newline at end of file +$version = (nbgv get-version -f json | ConvertFrom-Json).NuGetPackageVersion +docfx metadata docfx/docfx.json +docker build -t cuemon-docfx:$version -f Dockerfile.docfx . +get-childItem -recurse -path docfx/api -include *.yml, .manifest | remove-item +docker tag cuemon-docfx:$version tcr.cuemon.dk/cuemon-docfx:$version +docker push tcr.cuemon.dk/cuemon-docfx:$version \ No newline at end of file From 00586cc967ef33df3550fd3d72e9bc22193533fd Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 14 Sep 2020 22:42:01 +0200 Subject: [PATCH 187/385] Added/updated NuGet package release notes. --- .../Properties/PackageReleaseNotes.txt | 24 +++++++--- .../Properties/PackageReleaseNotes.txt | 46 +++++++++++++++++++ 2 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt diff --git a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt index 1fe4f8375..a53fe0ab2 100644 --- a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt @@ -1,31 +1,41 @@ 6.0.0 Availability: NET Standard 2.0 - +  # Upgrade Steps - [ACTION REQUIRED] - - +  # Breaking Changes - REMOVED StringFormatter class from the Cuemon namespace - REMOVED StandardizedDateTimeFormatPattern enum from the Cuemon namespace - MOVED AsyncOptions class in the Cuemon.Threading namespace to its own assembly (by the same name and namespace) - +- REMOVED JsonWebToken class from the Cuemon.Security.Web namespace +- REMOVED JsonWebTokenHashAlgorithm class from the Cuemon.Security.Web namespace +- REMOVED JsonWebTokenHashAlgorithmConverter class from the Cuemon.Security.Web namespace +- REMOVED JsonWebTokenHeader class from the Cuemon.Security.Web namespace +- REMOVED JsonWebTokenPayload class from the Cuemon.Security.Web namespace +- REMOVED Obfuscator class from the Cuemon.Security namespace +- REMOVED ObfuscatorMapping class from the Cuemon.Security namespace +- REMOVED SecurityToken class from the Cuemon.Security namespace +- REMOVED SecurityTokenSettings class from the Cuemon.Security namespace (replaced with SignedUriOptions in the Cuemon.Extensions.Net.Security namespace) +- REMOVED SecurityUtility class from the Cuemon.Security namespace +  # New Features - - - +  # Bug Fixes - - - +  # Improvements - - - +  # Quality Actions - - - +  # Other Changes - - \ No newline at end of file diff --git a/src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt b/src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..c48e7b916 --- /dev/null +++ b/src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt @@ -0,0 +1,46 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# Upgrade Steps +- The Cuemon.Security assembly was removed with this version +- Any types found in the former Cuemon.Security namespace was merged either into this namespace (Cuemon.Security.Cryptography) or the Cuemon.Security namespace (Cuemon.Core assembly) +- Any former extension methods of the Cuemon.Security namespace was removed completely due to the new intuitive static factory classes (HashFactory, KeyedHashFactory and UnkeyedHashFactory) +- The extent of refactoring applied to this project resulted in so many breaking changes that a git diff is advisable +  +# New Features +- ADDED AesCryptor class in the Cuemon.Security.Cryptography namespace that provides an implementation of the Advanced Encryption Standard (AES) symmetric algorithm +- ADDED AesCryptorOptions class in the Cuemon.Security.Cryptography namespace that specifies options related to AesCryptor +- ADDED AesKeyOptions class in the Cuemon.Security.Cryptography namespace that specifies options related to AesCryptor.GenerateKey +- ADDED HmacMessageDigest5 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the MD5 hash function +- ADDED HmacSecureHashAlgorithm1 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA1 hash function +- ADDED HmacSecureHashAlgorithm256 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA256 hash function +- ADDED HmacSecureHashAlgorithm384 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA384 hash function +- ADDED HmacSecureHashAlgorithm512 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA512 hash function +- ADDED KeyedCryptoHash class in the Cuemon.Security.Cryptography namespace that represents the base class from which all implementations of Hash-based Message Authentication Code (HMAC) should derive +- ADDED MessageDigest5 class in the Cuemon.Security.Cryptography namespace that provides a MD5 implementation of the MD (Message Digest) cryptographic hashing algorithm for 128-bit hash values +- ADDED SecureHashAlgorithm1 class in the Cuemon.Security.Cryptography namespace that provides a SHA-1 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 160-bit hash values +- ADDED SecureHashAlgorithm256 class in the Cuemon.Security.Cryptography namespace that provides a SHA-256 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 256-bit hash values +- ADDED SecureHashAlgorithm384 class in the Cuemon.Security.Cryptography namespace that provides a SHA-384 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 384-bit hash values +- ADDED SecureHashAlgorithm512 class in the Cuemon.Security.Cryptography namespace that provides a SHA-512 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 512-bit hash values +- ADDED UnkeyedCryptoHash class in the Cuemon.Security.Cryptography namespace that represents the base class from which all implementations of cryptographic hashing algorithm should derive +  +# Breaking Changes +- REPLACED AdvancedEncryptionStandardKeySize enum in the Cuemon.Security.Cryptography namespace with AesSize +- REMOVED AdvancedEncryptionStandardUtility class from the Cuemon.Security.Cryptography namespace +- REMOVED CyclicRedundancyCheck class from the Cuemon.Security.Cryptography namespace +- REMOVED CyclicRedundancyCheck32 class from the Cuemon.Security.Cryptography namespace +- REPLACED HashAlgorithmType enum in the Cuemon.Security.Cryptography namespace with UnkeyedCryptoAlgorithm +- REMOVED HashOptions class from the Cuemon.Security.Cryptography namespace +- MOVED HashResult class to the Cuemon.Security namespace +- REPLACED HashUtility class in the Cuemon.Security.Cryptography namespace with UnkeyedHashFactory +- REMOVED HashUtilityExtensions class from the Cuemon.Security.Cryptography namespace +- REPLACED HmacAlgorithmType enum in the Cuemon.Security.Cryptography namespace with KeyedCryptoAlgorithm +- REPLACED HmacUtility class in the Cuemon.Security.Cryptography namespace with KeyedHashFactory +- REMOVED HmacUtilityExtensions class from the Cuemon.Security.Cryptography namespace +- REMOVED KeyedHashOptions class from the Cuemon.Security.Cryptography namespace +- REMOVED PolynomialRepresentation enum from the Cuemon.Security.Cryptography namespace +- REMOVED StreamHashOptions class from the Cuemon.Security.Cryptography namespace +- REMOVED StreamKeyedHashOptions class from the Cuemon.Security.Cryptography namespace +- REMOVED StringHashOptions class from the Cuemon.Security.Cryptography namespace +- REMOVED StringKeyedHashOptions class from the Cuemon.Security.Cryptography namespace +- REMOVED StrongNumberUtility class from the Cuemon.Security.Cryptography namespace (replaced with Generate.RandomNumber in the Cuemon namespace) \ No newline at end of file From 1aceeeb139aea95f96ccc8dbc12646550d2685f3 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 14 Sep 2020 23:16:53 +0200 Subject: [PATCH 188/385] Updated DocFx markdown. --- .../Cuemon.Security.Cryptography.md | 1 + docfx/api/namespaces/Cuemon.Threading.md | 1 + docfx/api/namespaces/Cuemon.Xml.md | 1 + docfx/index.md | 501 +----------------- 4 files changed, 10 insertions(+), 494 deletions(-) diff --git a/docfx/api/namespaces/Cuemon.Security.Cryptography.md b/docfx/api/namespaces/Cuemon.Security.Cryptography.md index 404595c15..acc329462 100644 --- a/docfx/api/namespaces/Cuemon.Security.Cryptography.md +++ b/docfx/api/namespaces/Cuemon.Security.Cryptography.md @@ -5,4 +5,5 @@ summary: *content The Cuemon.Security.Cryptography namespace contains types related to cryptographic hashing (both keyed and non-keyed) and a ready-to-use implementation of the Advanced Encryption Standard (AES) symmetric algorithm. The namespace is an addition to the System.Security.Cryptography namespace. Availability: NET Standard 2.0 + Complements: [System.Security.Cryptography namespace](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography?view=netstandard-2.0) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Threading.md b/docfx/api/namespaces/Cuemon.Threading.md index 02ca6dc83..ea03a24e2 100644 --- a/docfx/api/namespaces/Cuemon.Threading.md +++ b/docfx/api/namespaces/Cuemon.Threading.md @@ -5,4 +5,5 @@ summary: *content The Cuemon.Threading namespace contains types related to working with long-running concurrent loops and regions that utilizes both synchronous and asynchronous delegates. The namespace is an addition to the System.Threading namespace. Availability: NET Standard 2.0 + Complements: [System.Threading namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading?view=netstandard-2.0) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.md b/docfx/api/namespaces/Cuemon.Xml.md index 5e6d9057e..a84af953b 100644 --- a/docfx/api/namespaces/Cuemon.Xml.md +++ b/docfx/api/namespaces/Cuemon.Xml.md @@ -5,4 +5,5 @@ summary: *content The Cuemon.Xml namespace contains types related to encoding, converting and serialization. The included lightweight XML serializer framework offers same flexibility as the one provided by the JSON equivalent from Newtonsoft. The namespace is an addition to both System.Xml- and System.Xml.Serialization namespaces. Availability: NET Standard 2.0 + Complements: [System.Xml namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml?view=netstandard-2.0), [System.Xml.Serialization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization?view=netstandard-2.0) \ No newline at end of file diff --git a/docfx/index.md b/docfx/index.md index beeb7c169..ca557643a 100644 --- a/docfx/index.md +++ b/docfx/index.md @@ -1,5 +1,5 @@ --- -title: Welcome to Cuemon .NET Standard at Github.IO +title: Technical documentation, API, and code examples documentType: index ---
@@ -8,16 +8,17 @@ documentType: index
-

Cuemon .NET Standard 5.0.2018.250

-

Cuemon .NET Standard is an open-source family of .NET Standard assemblies that, by heart, is free, flexible and built to extend and boost your agile codebelt.

+

Cuemon 6.0.0-preview

+

Cuemon is an open-source project that targets and complements the Microsoft .NET ecosystem. It provides vast ways of possibilities for all breeds of coders, programmers, developers and the likes thereof. Ideal for .NET, .NET Standard, .NET Core, Universal Windows Platform and .NET Framework 4.6.1 and newer.

+

It is, by heart, free, flexible and built to extend and boost your agile codebelt.

-
-
-
-
-
-

NuGet Packages - One Size Fits All

-
-
-
- -
- - -
-
- - Cuemon.Collections.Specialized
- Cuemon.Core
- Cuemon.Data
- Cuemon.Data.XmlClient
- Cuemon.Integrity
- Cuemon.IO
- Cuemon.Net
- Cuemon.Reflection
- Cuemon.Runtime
- Cuemon.Runtime.Caching
- Cuemon.Security
- Cuemon.Serialization
- Cuemon.Serialization.Xml
- Cuemon.Threading
- Cuemon.Web
- Cuemon.Xml -
-
-
- -
- - -
-
- - Cuemon.AspNetCore
- Cuemon.AspNetCore.Authentication
- Cuemon.AspNetCore.Mvc
- Cuemon.AspNetCore.Mvc.Formatters.Json
- Cuemon.AspNetCore.Mvc.Formatters.Xml
- Cuemon.AspNetCore.Razor.TagHelpers
- Cuemon.Core
- Cuemon.Integrity -
-
-
-
-
-
-
-
-
-
-

NuGet Packages - When Size Matters

-
-
- -
- - -
-
- - Cuemon.Core
- Cuemon.Integrity -
-
-
- -
- - -
-
- - Cuemon.AspNetCore
- Cuemon.Core -
-
-
- -
- - -
-
- - Cuemon.AspNetCore
- Cuemon.Core
- Cuemon.Integrity
- Cuemon.Serialization.Json
- Cuemon.Serialization.Xml -
-
-
-
-
- -
-
-
-
- -
- - -
-
- - Cuemon.AspNetCore
- Cuemon.Core
- Cuemon.Serialization
- Cuemon.Serialization.Json -
-
-
- -
- - -
-
- - Cuemon.AspNetCore
- Cuemon.Core
- Cuemon.Serialization
- Cuemon.Serialization.Xml
- Cuemon.Xml -
-
-
- -
- - -
-
- - Cuemon.AspNetCore.Mvc
- Cuemon.Core -
-
-
-
-
-
-
-
-
- -
- - -
-
- - Cuemon.Core - -
-
- -
- - -
-
- - / - -
-
- -
- - -
-
- - Cuemon.Collections.Specialized
- Cuemon.Core
- Cuemon.Runtime -
-
-
-
-
-
-
-
-
- -
- - -
-
- - Cuemon.Core
- Cuemon.Data
- Cuemon.Xml -
-
-
- -
- - -
-
- - Cuemon.Core
- Cuemon.IO
- Cuemon.Reflection
- Cuemon.Security -
-
-
- -
- - -
-
- - Cuemon.Core - -
-
-
-
-
-
-
-
- -
- - -
-
- - Cuemon.Core
- Cuemon.Runtime
- Cuemon.Security -
-
-
- -
- - -
-
- - Cuemon.Core - -
-
- -
- - -
-
- - Cuemon.Core - -
-
-
-
-
-
-
-
- -
- - -
-
- - Cuemon.Core
- Cuemon.Reflection -
-
-
- -
- - -
-
- - Cuemon.Core
- Cuemon.Runtime -
-
-
- -
- - -
-
- - Cuemon.Core
- Cuemon.IO
- Cuemon.Runtime -
-
-
-
-
-
-
-
-
- -
- - -
-
- - Cuemon.Core - -
-
- -
- - -
-
- - Cuemon.Core
- Cuemon.IO
- Cuemon.Serialization -
-
-
- -
- - -
-
- - Cuemon.Core
- Cuemon.Serialization
- Cuemon.Xml -
-
-
-
-
-
-
-
-
- -
- - -
-
- - Cuemon.Core - -
-
- -
- - -
-
- - Cuemon.Collections.Specialized
- Cuemon.Core
- Cuemon.Integrity -
-
-
- -
- - -
-
- - Cuemon.Core
- Cuemon.IO
- Cuemon.Runtime
- Cuemon.Runtime.Caching
- Cuemon.Security -
-
-
-
-
-
-
\ No newline at end of file +
\ No newline at end of file From 33b6ef2422caa771fc2becafb13667ae640ed501 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 14 Sep 2020 23:22:43 +0200 Subject: [PATCH 189/385] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2fb2899af..8bdc17860 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,8 @@ To consume a CI build, create a `NuGet.Config` in your root solution directory a ``` +Check out the preliminary documentation generated by DocFx: https://docs.cuemon.net/ + Stay tuned! Useful links for this project (will soon be changed for the forthcoming release): From ad7032479ae8dfafe48b932af80f05511f42dab1 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 14 Sep 2020 23:45:02 +0200 Subject: [PATCH 190/385] Replaced ThrowsAsync with ThrowsAnyAsync. Framework Design Guidelines clearly states, that an OperationCancelledException should be thown. However, TaskCanceledException is often thrown instead from the .NET runtime. --- .../TimeMeasureTest.cs | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/test/Cuemon.Diagnostics.Tests/TimeMeasureTest.cs b/test/Cuemon.Diagnostics.Tests/TimeMeasureTest.cs index 80ac17efb..fba5fc1e5 100644 --- a/test/Cuemon.Diagnostics.Tests/TimeMeasureTest.cs +++ b/test/Cuemon.Diagnostics.Tests/TimeMeasureTest.cs @@ -523,7 +523,7 @@ public async Task WithActionAsync_Use_0_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync(token => Task.Delay(expected, token), ctsShouldFail.Token); }); @@ -545,7 +545,7 @@ public async Task WithActionAsync_Use_1_Argument_And_CancellationToken_ShouldTak var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a, token) => Task.Delay(expected, token), 1, ctsShouldFail.Token); }); @@ -569,7 +569,7 @@ public async Task WithActionAsync_Use_2_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, token) => Task.Delay(expected, token), 1, 2, ctsShouldFail.Token); }); @@ -594,7 +594,7 @@ public async Task WithActionAsync_Use_3_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, token) => Task.Delay(expected, token), 1, 2, 3, ctsShouldFail.Token); }); @@ -620,7 +620,7 @@ public async Task WithActionAsync_Use_4_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, token) => Task.Delay(expected, token), 1, 2, 3, 4, ctsShouldFail.Token); }); @@ -647,7 +647,7 @@ public async Task WithActionAsync_Use_5_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, ctsShouldFail.Token); }); @@ -675,7 +675,7 @@ public async Task WithActionAsync_Use_6_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, a6, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, 6, ctsShouldFail.Token); }); @@ -704,7 +704,7 @@ public async Task WithActionAsync_Use_7_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, a6, a7, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, 6, 7, ctsShouldFail.Token); }); @@ -734,7 +734,7 @@ public async Task WithActionAsync_Use_8_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, a6, a7, a8, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, 6, 7, 8, ctsShouldFail.Token); }); @@ -765,7 +765,7 @@ public async Task WithActionAsync_Use_9_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, a6, a7, a8, a9, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, 6, 7, 8, 9, ctsShouldFail.Token); }); @@ -797,7 +797,7 @@ public async Task WithActionAsync_Use_10_Arguments_And_CancellationToken_ShouldT var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ctsShouldFail.Token); }); @@ -830,7 +830,7 @@ public async Task WithFuncAsync_Use_0_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async token => { @@ -862,7 +862,7 @@ public async Task WithFuncAsync_Use_1_Argument_And_CancellationToken_ShouldTakeA var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a, token) => { @@ -896,7 +896,7 @@ public async Task WithFuncAsync_Use_2_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, token) => { @@ -931,7 +931,7 @@ public async Task WithFuncAsync_Use_3_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, token) => { @@ -967,7 +967,7 @@ public async Task WithFuncAsync_Use_4_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, token) => { @@ -1004,7 +1004,7 @@ public async Task WithFuncAsync_Use_5_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, token) => { @@ -1042,7 +1042,7 @@ public async Task WithFuncAsync_Use_6_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, a6, token) => { @@ -1081,7 +1081,7 @@ public async Task WithFuncAsync_Use_7_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, a6, a7, token) => { @@ -1121,7 +1121,7 @@ public async Task WithFuncAsync_Use_8_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, a6, a7, a8, token) => { @@ -1162,7 +1162,7 @@ public async Task WithFuncAsync_Use_9_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, a6, a7, a8, a9, token) => { @@ -1204,7 +1204,7 @@ public async Task WithFuncAsync_Use_10_Arguments_And_CancellationToken_ShouldTak var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, token) => { From 9faa72c8fd8e40441543b4bb594cc328fcf537a1 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 15 Sep 2020 17:50:40 +0200 Subject: [PATCH 191/385] Changed Cuemon -> Cuemon for .NET. --- README.md | 72 ++--- docfx/docfx.json | 688 +---------------------------------------------- docfx/index.md | 4 +- 3 files changed, 44 insertions(+), 720 deletions(-) diff --git a/README.md b/README.md index 8bdc17860..51ea7be6b 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,32 @@ -![Cuemon](https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png) +![Cuemon for .NET](https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png) -Cuemon --------------------- -Cuemon is a free and flexible assembly package for the Microsoft .NET ecosystem. It was built to extend and boost your codebelt - providing vast ways of possibilities for all breeds of coders, programmers, developers and the likes thereof. Ideal for .NET, .NET Standard, .NET Core, Universal Windows Platform and .NET Framework 4.6.1 and newer. +# Cuemon for .NET -![License](https://img.shields.io/github/license/gimlichael/cuemon) +An open-source project (MIT license) that targets and complements the Microsoft .NET platform. It provides vast ways of possibilities for all breeds of coders, programmers, developers and the likes thereof. Ideal for .NET, .NET Standard, .NET Core, Universal Windows Platform and .NET Framework 4.6.1 and newer. -This development branch contains the latest version which has been completely refactored and updated to suport .NET Core 3.1. -All CI and CD will be runned on Azure DevOps and is currently in process of being tweaked. +It is, by heart, free, flexible and built to extend and boost your agile codebelt. -Once fully automated and tested thoroughly, it will be pushed to a new branch, release, and hereafter again tested and lastly to master and Nuget packages. +## State of the Union -Another big change for this upcoming release is the versioning; the world has spoken - and chosen semantic versioning. +Cuemon for .NET (formerly Cuemon .NET Standard) has been completely refactored and updated to support .NET Core 3.1 while receiving a name that reflects the forthcoming version of .NET - .NET 5. +Another big change for this upcoming release is the versioning; the world has spoken - and chosen semantic versioning. The release for now is planned to be 6.0.0. -[![Build Status](https://dev.azure.com/gimlichael/Cuemon/_apis/build/status/gimlichael.Cuemon?branchName=development)](https://dev.azure.com/gimlichael/Cuemon/_build/latest?definitionId=9&branchName=development) - -[![codecov](https://codecov.io/gh/gimlichael/Cuemon/branch/development/graph/badge.svg)](https://codecov.io/gh/gimlichael/Cuemon) - -[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=coverage)](https://sonarcloud.io/dashboard?id=Cuemon) - -[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=Cuemon)](https://sonarcloud.io/dashboard?id=Cuemon) +Check out the WIP documentation (generated by DocFx): https://docs.cuemon.net/ -[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=Cuemon) +All CI and CD integrations are done on [Microsoft Azure DevOps](https://azure.microsoft.com/en-us/services/devops/) and is currently in the process of being tweaked. -[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=Cuemon) +All code quality analysis are done by [SonarCloud](https://sonarcloud.io/) and [CodeCov.io](https://codecov.io/). -[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=security_rating)](https://sonarcloud.io/dashboard?id=Cuemon) +Stay tuned for more exiting news! -[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=ncloc)](https://sonarcloud.io/dashboard?id=Cuemon) +![License](https://img.shields.io/github/license/gimlichael/cuemon) [![Build Status](https://dev.azure.com/gimlichael/Cuemon/_apis/build/status/gimlichael.Cuemon?branchName=development)](https://dev.azure.com/gimlichael/Cuemon/_build/latest?definitionId=9&branchName=development) [![codecov](https://codecov.io/gh/gimlichael/Cuemon/branch/development/graph/badge.svg)](https://codecov.io/gh/gimlichael/Cuemon) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=coverage)](https://sonarcloud.io/dashboard?id=Cuemon) -[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=code_smells)](https://sonarcloud.io/dashboard?id=Cuemon) -[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_index)](https://sonarcloud.io/dashboard?id=Cuemon) +## Development Branch -[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=bugs)](https://sonarcloud.io/dashboard?id=Cuemon) - -[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=Cuemon) - -Want to try out the new and improved Cuemon? +The `development` branch contains the latest (and greatest) version of the code. To consume a CI build, create a `NuGet.Config` in your root solution directory and add following content: @@ -49,16 +36,39 @@ To consume a CI build, create a `NuGet.Config` in your root solution directory a - + - + ``` +Do note, that builds from development are preview builds and not to be considered stable. + +Once tested thoroughly and feature milestone has been reached, the code will be pushed and merged to a new branch; `release`. + +## Release Branch + +The `release` branch contains the next version of Cuemon for .NET. Here it will be tested again while the next semantic version is being determined. + +All CI builds are pushed to NuGet.org as either `alpha`, `beta` or `rc` releases. For more information, check out [Package versioning - Pre-release Versions](https://docs.microsoft.com/en-us/nuget/concepts/package-versioning#pre-release-versions) at Microsoft. + +Lastly, when things are looking all fine and dandy, the code will be pushed and merged to the `master` branch. + +## Master Branch + +The `master` branch always contains the current `production` ready version of Cuemon for .NET. + +Builds performed from this repository are pushed to NuGet.org as the actual version they represent. Eg. the forthcoming version of Cuemon for .NET will be 6.0.0. + +### Code Quality Monitoring + +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=alert_status)](https://sonarcloud.io/dashboard?id=Cuemon) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=Cuemon) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=Cuemon) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=security_rating)](https://sonarcloud.io/dashboard?id=Cuemon) + +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=ncloc)](https://sonarcloud.io/dashboard?id=Cuemon) [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=code_smells)](https://sonarcloud.io/dashboard?id=Cuemon) [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=sqale_index)](https://sonarcloud.io/dashboard?id=Cuemon) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=bugs)](https://sonarcloud.io/dashboard?id=Cuemon) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=Cuemon) [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=duplicated_lines_density)](https://sonarcloud.io/dashboard?id=Cuemon) + -Check out the preliminary documentation generated by DocFx: https://docs.cuemon.net/ -Stay tuned! +### Links to NuGet packages (will be updated once Cuemon for .NET has shipped in 6.0.0) Useful links for this project (will soon be changed for the forthcoming release): diff --git a/docfx/docfx.json b/docfx/docfx.json index b0dc987fa..2cc08dd2c 100644 --- a/docfx/docfx.json +++ b/docfx/docfx.json @@ -1,690 +1,4 @@ { - // "metadata": [ - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Core/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Data/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.data", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Data.Integrity/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.data.integrity", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Integrity/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.integrity", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Data.SqlClient/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.data.sqlclient", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Diagnostics/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.diagnostics", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.IO/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.io", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.1" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Net/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.net", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Resilience/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.resilience", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Runtime.Caching/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.runtime.caching", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Security.Cryptography/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.security.cryptography", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Threading/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.threading", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Xml/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.xml", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Collections.Generic/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.collections.generic", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Collections.Specialized/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.collections.specialized", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Core/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Data/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.data", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Data.Integrity/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.data.integrity", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.DependencyInjection/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.dependencyinjection", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Diagnostics/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.diagnostics", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.IO/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.io", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.1" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Net/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.net", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Newtonsoft.Json/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.newtonsoft.json", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Reflection/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.reflection", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Text/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.text", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Threading/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.threading", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Xml/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.xml", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netstandard2.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.AspNetCore/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/aspnet/cuemon.aspnetcore", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netcoreapp3.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.AspNetCore.Authentication/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/aspnet/cuemon.aspnetcore.authentication", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netcoreapp3.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.AspNetCore.Mvc/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/aspnet/cuemon.aspnetcore.mvc", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netcoreapp3.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.AspNetCore.Razor/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/aspnet/cuemon.aspnetcore.razor", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netcoreapp3.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.AspNetCore/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/aspnet/cuemon.extensions.aspnetcore", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netcoreapp3.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.AspNetCore.Mvc/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/aspnet/cuemon.extensions.aspnetcore.mvc", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netcoreapp3.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/aspnet/cuemon.extensions.aspnetcore.mvc.formatters.newtonsoft.json", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netcoreapp3.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/aspnet/cuemon.extensions.aspnetcore.mvc.formatters.xml", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netcoreapp3.0" - // } - // }, - // { - // "src": [ - // { - // "files": [ - // "Cuemon.Extensions.Xunit/**.csproj" - // ], - // "exclude": [ - // "**/bin/**", - // "**/obj/**" - // ], - // "src": "../src" - // } - // ], - // "dest": "api/dotnet/cuemon.extensions.xunit", - // "filter": "filterConfig.yml", - // "properties": { - // "TargetFramework": "netcoreapp3.0" - // } - // } - // ], "metadata": [ { "src": [ @@ -801,7 +115,7 @@ } ], "globalMetadata": { - "_appTitle": "Cuemon", + "_appTitle": "Cuemon for .NET", "_appFooter": "Copyright 2008-2020 Geekle. All rights reserved. Code with passion; love your code; deliver with pride. 👨‍💻️🔥❤️🚀🤘
Generated by DocFX
", "_appLogoPath": "images/50x50.png", "_appFaviconPath": "images/favicon.ico", diff --git a/docfx/index.md b/docfx/index.md index ca557643a..9e21b7f68 100644 --- a/docfx/index.md +++ b/docfx/index.md @@ -8,8 +8,8 @@ documentType: index
-

Cuemon 6.0.0-preview

-

Cuemon is an open-source project that targets and complements the Microsoft .NET ecosystem. It provides vast ways of possibilities for all breeds of coders, programmers, developers and the likes thereof. Ideal for .NET, .NET Standard, .NET Core, Universal Windows Platform and .NET Framework 4.6.1 and newer.

+

Cuemon for .NET 6.0.0-preview

+

Cuemon for .NET is an open-source project (MIT license) that targets and complements the Microsoft .NET platform. It provides vast ways of possibilities for all breeds of coders, programmers, developers and the likes thereof. Ideal for .NET, .NET Standard, .NET Core, Universal Windows Platform and .NET Framework 4.6.1 and newer.

It is, by heart, free, flexible and built to extend and boost your agile codebelt.

From 786bb33d03d286997aac5f1126d3311649b2575f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 18 Sep 2020 00:48:00 +0200 Subject: [PATCH 192/385] Fixed a bug that could result in invalid byte-array size due to modulus with BigInteger.Pow(2, bits). --- src/Cuemon.Core/Security/FowlerNollVoHash.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Cuemon.Core/Security/FowlerNollVoHash.cs b/src/Cuemon.Core/Security/FowlerNollVoHash.cs index 37c5f493c..bb4a720bd 100644 --- a/src/Cuemon.Core/Security/FowlerNollVoHash.cs +++ b/src/Cuemon.Core/Security/FowlerNollVoHash.cs @@ -17,9 +17,9 @@ public abstract class FowlerNollVoHash : Hash /// The which need to be configured. protected FowlerNollVoHash(short bits, BigInteger prime, BigInteger offsetBasis, Action setup) : base(setup) { + Bits = bits; Prime = prime; OffsetBasis = offsetBasis; - Bits = BigInteger.Pow(2, bits); } /// @@ -38,7 +38,7 @@ protected FowlerNollVoHash(short bits, BigInteger prime, BigInteger offsetBasis, /// Gets the size of the implementation in bits. /// /// The size of the implementation in bits. - public BigInteger Bits { get; } + public short Bits { get; } /// /// Computes the hash value for the specified . @@ -55,7 +55,6 @@ public override HashResult ComputeHash(byte[] input) { hash ^= b; hash *= Prime; - hash %= Bits; } break; default: @@ -63,12 +62,11 @@ public override HashResult ComputeHash(byte[] input) { hash *= Prime; hash ^= b; - hash %= Bits; } break; } var result = hash.ToByteArray(); - if (Condition.IsOdd(result.Length)) { Array.Resize(ref result, result.Length - 1); } + Array.Resize(ref result, Bits / ByteUnit.BitsPerByte); result = Convertible.ReverseEndianness(result, o => o.ByteOrder = Options.ByteOrder); return new HashResult(result); } From 0d4e256574405f4c9bd67d6c095deef0606f504c Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 18 Sep 2020 00:48:58 +0200 Subject: [PATCH 193/385] Adjusted the two failing test due to fix of bug in 786bb33d. --- .../Security/HashFactoryTest.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/Cuemon.Core.Tests/Security/HashFactoryTest.cs b/test/Cuemon.Core.Tests/Security/HashFactoryTest.cs index 7c9052e01..5a4eb52f2 100644 --- a/test/Cuemon.Core.Tests/Security/HashFactoryTest.cs +++ b/test/Cuemon.Core.Tests/Security/HashFactoryTest.cs @@ -11,6 +11,17 @@ public HashFactoryTest(ITestOutputHelper output) : base(output) { } + [Fact] + public void CreateFnv64_Fnv1_ShouldHaveSizeOf64Bits() + { + var s1 = "957-KEY"; + var s2 = "958-KEY"; + var hf = HashFactory.CreateFnv64(o => o.Algorithm = FowlerNollVoAlgorithm.Fnv1a); + + TestOutput.WriteLine(hf.ComputeHash(s1).ToHexadecimalString()); + TestOutput.WriteLine(hf.ComputeHash(s2).ToHexadecimalString()); + } + [Fact] public void CreateCrc_Crc64_ShouldBeValidHashResult() { @@ -188,7 +199,7 @@ public void CreateFnv1024_Fnv1_ShouldBeValidHashResult() var h = HashFactory.CreateFnv1024(o => o.Algorithm = FowlerNollVoAlgorithm.Fnv1); Assert.Equal("70e427242b62d481df8f97b5a7c389f5f6df3457fda072841eb0ac24759648a39784a0ab922c4730b68efa7d0980de290e79de582d88c97e17c953592b9b70ce6dca5ccd19cd93182254abfe9ed6face84979b6793e44e46621ad88c76744b1296ed3934a03e443ce593f1d3dd137dcba2ac2c5edb2cc9c7353111c2327224ca", h.ComputeHash(Alphanumeric.LettersAndNumbers).ToHexadecimalString()); Assert.Equal("c801f8e08ae91b180b98dd7d9f65ceb687ca86358c6905f60a7d1014c182b04f441590d012afb5871d0f57000000000000000000000000000000000000000000000000000000000000000000000000000000018045149ade1c79abe3b709a406f7d9205169bec59b126140bcb96f9d5d3e2ea91e0b2b52fa8d2d0d70ecdaeab2", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("98d7c19fbce653df221b9f717d3490ff95ca87fdaef30d1b823372f85b24a372f50e380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007685cd81a491dbccc21ad06648d09a5c8cf5a78482054e91470b33dde77252caef66597", h.ComputeHash(byte.MinValue).ToHexadecimalString()); + Assert.Equal("000000000000000098d7c19fbce653df221b9f717d3490ff95ca87fdaef30d1b823372f85b24a372f50e380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007685cd81a491dbccc21ad06648d09a5c8cf5a78482054e91470b33dde77252caef66597", h.ComputeHash(byte.MinValue).ToHexadecimalString()); } [Fact] @@ -242,7 +253,8 @@ public void CreateFnv1024_Fnv1a_ShouldBeValidHashResult() var h = HashFactory.CreateFnv1024(o => o.Algorithm = FowlerNollVoAlgorithm.Fnv1a); Assert.Equal("199c0ace56c5c33d8bce6f7cf4bc4b555e0fc3ae8d37c4b7384678a34d96ae8192825ae6bcda63dbb9e3417d0980de290e79de582d88c97e17c9535950c35f4f16d311bc66d1ac2892d59f7b0697257eba9fc1e3accbc85729218306b34996eedf99292c814e8a75f41ddc5a5b5177b6e60c0211ad8d8f78395c7c2d2c483e7e", h.ComputeHash(Decorator.Enclose(Alphanumeric.LettersAndNumbers).ToStream()).ToHexadecimalString()); Assert.Equal("c801f8e08ae91b180b98dd7d9f65ceb687ca86358c6905f60a7d1014c182b04ee2ab1bd0066e9857a7f7de000000000000000000000000000000000000000000000000000000000000000000000000000000018045149ade1c79abe3b709a406f7d9205169bec59b126140bcb96f9d5d3e2ea91dfc0f40af8e7e3f25d14c3186", h.ComputeHash(Alphanumeric.Numbers).ToHexadecimalString()); - Assert.Equal("98d7c19fbce653df221b9f717d3490ff95ca87fdaef30d1b823372f85b24a372f50e380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007685cd81a491dbccc21ad06648d09a5c8cf5a78482054e91470b33dde77252caef66597", h.ComputeHash(byte.MinValue).ToHexadecimalString()); + Assert.Equal("000000000000000098d7c19fbce653df221b9f717d3490ff95ca87fdaef30d1b823372f85b24a372f50e380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007685cd81a491dbccc21ad06648d09a5c8cf5a78482054e91470b33dde77252caef66597", h.ComputeHash(byte.MinValue).ToHexadecimalString()); + } } } \ No newline at end of file From fec1e7a7536c33def9964c34131330242d091c18 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 20 Sep 2020 13:28:20 +0200 Subject: [PATCH 194/385] Changed TaskCanceledException to OperationCancelledException (as it should be exact or derived as stated by xUnit doc). --- .../TimeMeasureTest.cs | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/test/Cuemon.Diagnostics.Tests/TimeMeasureTest.cs b/test/Cuemon.Diagnostics.Tests/TimeMeasureTest.cs index fba5fc1e5..1cacd2554 100644 --- a/test/Cuemon.Diagnostics.Tests/TimeMeasureTest.cs +++ b/test/Cuemon.Diagnostics.Tests/TimeMeasureTest.cs @@ -523,7 +523,7 @@ public async Task WithActionAsync_Use_0_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync(token => Task.Delay(expected, token), ctsShouldFail.Token); }); @@ -545,7 +545,7 @@ public async Task WithActionAsync_Use_1_Argument_And_CancellationToken_ShouldTak var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a, token) => Task.Delay(expected, token), 1, ctsShouldFail.Token); }); @@ -569,7 +569,7 @@ public async Task WithActionAsync_Use_2_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, token) => Task.Delay(expected, token), 1, 2, ctsShouldFail.Token); }); @@ -594,7 +594,7 @@ public async Task WithActionAsync_Use_3_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, token) => Task.Delay(expected, token), 1, 2, 3, ctsShouldFail.Token); }); @@ -620,7 +620,7 @@ public async Task WithActionAsync_Use_4_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, token) => Task.Delay(expected, token), 1, 2, 3, 4, ctsShouldFail.Token); }); @@ -647,7 +647,7 @@ public async Task WithActionAsync_Use_5_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, ctsShouldFail.Token); }); @@ -675,7 +675,7 @@ public async Task WithActionAsync_Use_6_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, a6, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, 6, ctsShouldFail.Token); }); @@ -704,7 +704,7 @@ public async Task WithActionAsync_Use_7_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, a6, a7, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, 6, 7, ctsShouldFail.Token); }); @@ -734,7 +734,7 @@ public async Task WithActionAsync_Use_8_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, a6, a7, a8, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, 6, 7, 8, ctsShouldFail.Token); }); @@ -765,7 +765,7 @@ public async Task WithActionAsync_Use_9_Arguments_And_CancellationToken_ShouldTa var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, a6, a7, a8, a9, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, 6, 7, 8, 9, ctsShouldFail.Token); }); @@ -797,7 +797,7 @@ public async Task WithActionAsync_Use_10_Arguments_And_CancellationToken_ShouldT var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithActionAsync((a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, token) => Task.Delay(expected, token), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ctsShouldFail.Token); }); @@ -830,7 +830,7 @@ public async Task WithFuncAsync_Use_0_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async token => { @@ -862,7 +862,7 @@ public async Task WithFuncAsync_Use_1_Argument_And_CancellationToken_ShouldTakeA var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a, token) => { @@ -896,7 +896,7 @@ public async Task WithFuncAsync_Use_2_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, token) => { @@ -931,7 +931,7 @@ public async Task WithFuncAsync_Use_3_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, token) => { @@ -967,7 +967,7 @@ public async Task WithFuncAsync_Use_4_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, token) => { @@ -1004,7 +1004,7 @@ public async Task WithFuncAsync_Use_5_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, token) => { @@ -1042,7 +1042,7 @@ public async Task WithFuncAsync_Use_6_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, a6, token) => { @@ -1081,7 +1081,7 @@ public async Task WithFuncAsync_Use_7_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, a6, a7, token) => { @@ -1121,7 +1121,7 @@ public async Task WithFuncAsync_Use_8_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, a6, a7, a8, token) => { @@ -1162,7 +1162,7 @@ public async Task WithFuncAsync_Use_9_Arguments_And_CancellationToken_ShouldTake var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, a6, a7, a8, a9, token) => { @@ -1204,7 +1204,7 @@ public async Task WithFuncAsync_Use_10_Arguments_And_CancellationToken_ShouldTak var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); var ctsShouldPass = new CancellationTokenSource(); - await Assert.ThrowsAnyAsync(async () => + await Assert.ThrowsAnyAsync(async () => { await TimeMeasure.WithFuncAsync(async (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, token) => { From a9efd620960d2aee63dfa327af1634c40bdd8ee1 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 21 Sep 2020 21:46:16 +0200 Subject: [PATCH 195/385] Added using (..), --- src/Cuemon.Core/Generate.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Cuemon.Core/Generate.cs b/src/Cuemon.Core/Generate.cs index a8ec7a7c0..96b5d6ecd 100644 --- a/src/Cuemon.Core/Generate.cs +++ b/src/Cuemon.Core/Generate.cs @@ -24,10 +24,12 @@ public static class Generate private static readonly ThreadLocal LocalRandomizer = new ThreadLocal(() => { var rnd = new byte[4]; - var rng = RandomNumberGenerator.Create(); - rng.GetBytes(rnd); - var seed = BitConverter.ToInt32(rnd, 0); - return new Random(seed); + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetNonZeroBytes(rnd); + var seed = BitConverter.ToInt32(rnd, 0); + return new Random(seed); + } }); /// @@ -156,10 +158,10 @@ public static string RandomString(int length, params string[] values) { Validator.ThrowIfSequenceNullOrEmpty(values, nameof(values)); var result = new ConcurrentBag(); - Parallel.For(0, length, i => + Parallel.For(0, length, i => { - var index = RandomNumber(values.Length); - var indexLength = values[index].Length; + var index = RandomNumber(values.Length); + var indexLength = values[index].Length; result.Add(values[index][RandomNumber(indexLength)]); }); return Decorator.Enclose(result).ToStringEquivalent(); From 683d3e447673c35a7b8783ff71ca9a3d0bd40b90 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 16:58:46 +0200 Subject: [PATCH 196/385] In-memory caching; completely refactored to be more flexible and ideal for dependency injection. --- src/Cuemon.Runtime.Caching/Cache.cs | 215 -- .../CacheCollection.GetOrAdd.cs | 2196 ----------------- .../CacheCollection.Memoization.cs | 929 ------- src/Cuemon.Runtime.Caching/CacheCollection.cs | 599 ----- src/Cuemon.Runtime.Caching/CacheEntry.cs | 168 ++ ...cheEventArgs.cs => CacheEntryEventArgs.cs} | 6 +- .../CacheInvalidation.cs | 81 + src/Cuemon.Runtime.Caching/CachingManager.cs | 16 +- .../Cuemon.Runtime.Caching.csproj | 1 + .../ICacheEnumerable.cs | 125 + src/Cuemon.Runtime.Caching/SlimMemoryCache.cs | 394 +++ .../SlimMemoryCacheOptions.cs | 70 + 12 files changed, 851 insertions(+), 3949 deletions(-) delete mode 100644 src/Cuemon.Runtime.Caching/Cache.cs delete mode 100644 src/Cuemon.Runtime.Caching/CacheCollection.GetOrAdd.cs delete mode 100644 src/Cuemon.Runtime.Caching/CacheCollection.Memoization.cs delete mode 100644 src/Cuemon.Runtime.Caching/CacheCollection.cs create mode 100644 src/Cuemon.Runtime.Caching/CacheEntry.cs rename src/Cuemon.Runtime.Caching/{CacheEventArgs.cs => CacheEntryEventArgs.cs} (60%) create mode 100644 src/Cuemon.Runtime.Caching/CacheInvalidation.cs create mode 100644 src/Cuemon.Runtime.Caching/ICacheEnumerable.cs create mode 100644 src/Cuemon.Runtime.Caching/SlimMemoryCache.cs create mode 100644 src/Cuemon.Runtime.Caching/SlimMemoryCacheOptions.cs diff --git a/src/Cuemon.Runtime.Caching/Cache.cs b/src/Cuemon.Runtime.Caching/Cache.cs deleted file mode 100644 index d6a5d3503..000000000 --- a/src/Cuemon.Runtime.Caching/Cache.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Cuemon.Runtime.Caching -{ - /// - /// An internal representation of a Cache object. - /// - internal class Cache - { - #region Constructors - /// - /// Initializes a new instance of the class. - /// - /// The identifier of this . - /// The cached value of this . - /// The group to associate and organize this by. - /// A sequence of objects for the item. When any dependency changes, the object becomes invalid and is removed from the cache. If there are no dependencies, this parameter contains a null reference (Nothing in Visual Basic). - /// The absolute expiration date time value of this . - /// The sliding expiration value of this . - internal Cache(string key, object value, string group, IEnumerable dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration) - { - Key = key; - Value = value; - Group = group; - Dependencies = dependencies == null ? null : new List(dependencies); - AbsoluteExpiration = absoluteExpiration.ToUniversalTime(); - SlidingExpiration = slidingExpiration; - Created = DateTime.UtcNow; - LastAccessed = Created; - } - #endregion - - #region Events - /// - /// Occurs when a object with a has expired. - /// - public event EventHandler Expired; - #endregion - - #region Properties - /// - /// Gets the identifier of this . - /// - /// The identifier of this . - public string Key { get; private set; } - - /// - /// Gets the value of this . - /// - /// The value of this . - public object Value { get; set; } - - /// - /// Gets the group to associate and organize this by. - /// - /// The group to associate and organize this by. - public string Group { get; private set; } - - /// - /// Gets a sequence of objects implementing the interface assigned to this . - /// - /// A sequence of objects implementing the interface assigned to this . - public IEnumerable Dependencies { get; private set; } - - /// - /// Gets the UTC absolute expiration date time value of this . - /// - /// The UTC absolute expiration date time value of this . - public DateTime AbsoluteExpiration { get; private set; } - - /// - /// Gets the UTC date time value from when this was created. - /// - /// The UTC date time value from when this was created. - public DateTime Created { get; private set; } - - /// - /// Gets the UTC date time value from when this was last accessed. - /// - /// The UTC date time value from when this was last accessed. - public DateTime LastAccessed { get; private set; } - - /// - /// Gets the sliding expiration value of this . - /// - /// The sliding expiration value of this . - public TimeSpan SlidingExpiration { get; private set; } - - /// - /// Gets a value indicating whether this should use the AbsoluteExpiration property for the caching logic. - /// - /// - /// true if this should use the AbsoluteExpiration property for the caching logic; otherwise, false. - /// - public bool UseAbsoluteExpiration - { - get { return (DateTime.MaxValue.ToUniversalTime() != AbsoluteExpiration); } - } - - /// - /// Gets a value indicating whether this should use the SlidingExpiration property for the caching logic. - /// - /// - /// true if this should use the SlidingExpiration property for the caching logic; otherwise, false. - /// - public bool UseSlidingExpiration - { - get { return (TimeSpan.Zero != SlidingExpiration); } - } - - /// - /// Gets a value indicating whether this is relying on a object. - /// - /// true if this is relying on a object; otherwise, false. - public bool UseDependency - { - get { return (Dependencies != null && Dependencies.Any()); } - } - - /// - /// Determines whether the specified time resolves this as expired. - /// - /// The date and time to evaluate against. - /// - /// true if the specified time resolves this as expired; otherwise, false. - /// - public bool HasExpired(DateTime time) - { - if (!CanExpire) { return false; } - if (UseAbsoluteExpiration) - { - if (time >= AbsoluteExpiration) { return true; } - } - else if (UseSlidingExpiration) - { - TimeSpan currentPeriod = (time - LastAccessed); - if (currentPeriod >= SlidingExpiration) { return true; } - } - else if (UseDependency) - { - foreach (IDependency dependency in Dependencies) - { - if (dependency.HasChanged) { return true; } - } - } - return false; - } - - /// - /// Gets a value indicating whether this can expire. - /// - /// - /// true if this can expire; otherwise, false. - /// - public bool CanExpire - { - get { return UseAbsoluteExpiration || UseSlidingExpiration || (UseDependency); } - } - #endregion - - #region Methods - - internal void StartDependencies() - { - if (!UseDependency) { return; } - foreach (IDependency dependency in Dependencies) - { - dependency.DependencyChanged += ProcessDependencyChanged; - dependency.Start(); - } - } - - /// - /// Refreshes the UTC date time value from when this was created. - /// - public void Refresh() - { - LastAccessed = DateTime.UtcNow; - } - - private void ProcessDependencyChanged(object sender, DependencyEventArgs e) - { - OnExpiredRaised(new CacheEventArgs(this)); - if (UseDependency) - { - foreach (IDependency dependency in Dependencies) - { - dependency.DependencyChanged -= ProcessDependencyChanged; - } - } - } - - /// - /// Raises the event. - /// - /// The instance containing the event data. - protected virtual void OnExpiredRaised(CacheEventArgs e) - { - Expired?.Invoke(this, e); - } - - /// - /// Returns a that represents this instance. - /// - /// A that represents this instance. - public override string ToString() - { - return Generate.ObjectPortrayal(this, o => o.BypassOverrideCheck = true); - } - - #endregion - } -} \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/CacheCollection.GetOrAdd.cs b/src/Cuemon.Runtime.Caching/CacheCollection.GetOrAdd.cs deleted file mode 100644 index ef00fc008..000000000 --- a/src/Cuemon.Runtime.Caching/CacheCollection.GetOrAdd.cs +++ /dev/null @@ -1,2196 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; - -namespace Cuemon.Runtime.Caching -{ - public sealed partial class CacheCollection - { - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver) - { - return GetOrAdd(key, NoGroup, resolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver); - return (TResult)GetOrAddCore(factory, key, group).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, DateTime absoluteExpiration) - { - return GetOrAdd(key, NoGroup, resolver, absoluteExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, DateTime absoluteExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver); - return (TResult)GetOrAddCore(factory, key, group, () => absoluteExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, TimeSpan slidingExpiration) - { - return GetOrAdd(key, NoGroup, resolver, slidingExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, TimeSpan slidingExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver); - return (TResult)GetOrAddCore(factory, key, group, null, () => slidingExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, Func> dependencyResolver) - { - return GetOrAdd(key, NoGroup, resolver, dependencyResolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, Func> dependencyResolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - Validator.ThrowIfNull(dependencyResolver, nameof(dependencyResolver)); - var f1 = FuncFactory.Create(resolver); - var f2 = FuncFactory.Create(dependencyResolver); - return (TResult)GetOrAddCore(f1, key, group, null, null, f2).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The parameter of the function delegate . - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T arg) - { - return GetOrAdd(key, NoGroup, resolver, arg); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The parameter of the function delegate . - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T arg) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg); - return (TResult)GetOrAddCore(factory, key, group).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T arg, DateTime absoluteExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg, absoluteExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T arg, DateTime absoluteExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg); - return (TResult)GetOrAddCore(factory, key, group, () => absoluteExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T arg, TimeSpan slidingExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg, slidingExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T arg, TimeSpan slidingExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg); - return (TResult)GetOrAddCore(factory, key, group, null, () => slidingExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T arg, Func> dependencyResolver) - { - return GetOrAdd(key, NoGroup, resolver, arg, dependencyResolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T arg, Func> dependencyResolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - Validator.ThrowIfNull(dependencyResolver, nameof(dependencyResolver)); - var f1 = FuncFactory.Create(resolver, arg); - var f2 = FuncFactory.Create(dependencyResolver, arg); - return (TResult)GetOrAddCore(f1, key, group, null, null, f2).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2); - return (TResult)GetOrAddCore(factory, key, group).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, DateTime absoluteExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, absoluteExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, DateTime absoluteExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2); - return (TResult)GetOrAddCore(factory, key, group, () => absoluteExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, TimeSpan slidingExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, slidingExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, TimeSpan slidingExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2); - return (TResult)GetOrAddCore(factory, key, group, null, () => slidingExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, Func> dependencyResolver) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, dependencyResolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, Func> dependencyResolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - Validator.ThrowIfNull(dependencyResolver, nameof(dependencyResolver)); - var f1 = FuncFactory.Create(resolver, arg1, arg2); - var f2 = FuncFactory.Create(dependencyResolver, arg1, arg2); - return (TResult)GetOrAddCore(f1, key, group, null, null, f2).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3); - return (TResult)GetOrAddCore(factory, key, group).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, DateTime absoluteExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, absoluteExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, DateTime absoluteExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3); - return (TResult)GetOrAddCore(factory, key, group, () => absoluteExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, TimeSpan slidingExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, slidingExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, TimeSpan slidingExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3); - return (TResult)GetOrAddCore(factory, key, group, null, () => slidingExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, Func> dependencyResolver) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, dependencyResolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, Func> dependencyResolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - Validator.ThrowIfNull(dependencyResolver, nameof(dependencyResolver)); - var f1 = FuncFactory.Create(resolver, arg1, arg2, arg3); - var f2 = FuncFactory.Create(dependencyResolver, arg1, arg2, arg3); - return (TResult)GetOrAddCore(f1, key, group, null, null, f2).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4); - return (TResult)GetOrAddCore(factory, key, group).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, DateTime absoluteExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, absoluteExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, DateTime absoluteExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4); - return (TResult)GetOrAddCore(factory, key, group, () => absoluteExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, TimeSpan slidingExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, slidingExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, TimeSpan slidingExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4); - return (TResult)GetOrAddCore(factory, key, group, null, () => slidingExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func> dependencyResolver) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, dependencyResolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Func> dependencyResolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - Validator.ThrowIfNull(dependencyResolver, nameof(dependencyResolver)); - var f1 = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4); - var f2 = FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4); - return (TResult)GetOrAddCore(f1, key, group, null, null, f2).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5); - return (TResult)GetOrAddCore(factory, key, group).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, DateTime absoluteExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, absoluteExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, DateTime absoluteExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5); - return (TResult)GetOrAddCore(factory, key, group, () => absoluteExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, TimeSpan slidingExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, slidingExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, TimeSpan slidingExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5); - return (TResult)GetOrAddCore(factory, key, group, null, () => slidingExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the fifth parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The fifth parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func> dependencyResolver) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, dependencyResolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the fifth parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The fifth parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Func> dependencyResolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - Validator.ThrowIfNull(dependencyResolver, nameof(dependencyResolver)); - var f1 = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5); - var f2 = FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5); - return (TResult)GetOrAddCore(f1, key, group, null, null, f2).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6); - return (TResult)GetOrAddCore(factory, key, group).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, DateTime absoluteExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, absoluteExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, DateTime absoluteExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6); - return (TResult)GetOrAddCore(factory, key, group, () => absoluteExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, TimeSpan slidingExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, slidingExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, TimeSpan slidingExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6); - return (TResult)GetOrAddCore(factory, key, group, null, () => slidingExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the fifth parameter of the function delegate and the function delegate . - /// The type of the sixth parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The fifth parameter of the function delegate and the function delegate . - /// The sixth parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, Func> dependencyResolver) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, dependencyResolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the fifth parameter of the function delegate and the function delegate . - /// The type of the sixth parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The fifth parameter of the function delegate and the function delegate . - /// The sixth parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, Func> dependencyResolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - Validator.ThrowIfNull(dependencyResolver, nameof(dependencyResolver)); - var f1 = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6); - var f2 = FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5, arg6); - return (TResult)GetOrAddCore(f1, key, group, null, null, f2).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - return (TResult)GetOrAddCore(factory, key, group).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, DateTime absoluteExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, absoluteExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, DateTime absoluteExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - return (TResult)GetOrAddCore(factory, key, group, () => absoluteExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, TimeSpan slidingExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, slidingExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, TimeSpan slidingExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - return (TResult)GetOrAddCore(factory, key, group, null, () => slidingExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the fifth parameter of the function delegate and the function delegate . - /// The type of the sixth parameter of the function delegate and the function delegate . - /// The type of the seventh parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The fifth parameter of the function delegate and the function delegate . - /// The sixth parameter of the function delegate and the function delegate . - /// The seventh parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, Func> dependencyResolver) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, dependencyResolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the fifth parameter of the function delegate and the function delegate . - /// The type of the sixth parameter of the function delegate and the function delegate . - /// The type of the seventh parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The fifth parameter of the function delegate and the function delegate . - /// The sixth parameter of the function delegate and the function delegate . - /// The seventh parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, Func> dependencyResolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - Validator.ThrowIfNull(dependencyResolver, nameof(dependencyResolver)); - var f1 = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - var f2 = FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - return (TResult)GetOrAddCore(f1, key, group, null, null, f2).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - return (TResult)GetOrAddCore(factory, key, group).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, DateTime absoluteExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, absoluteExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, DateTime absoluteExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - return (TResult)GetOrAddCore(factory, key, group, () => absoluteExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, TimeSpan slidingExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, slidingExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, TimeSpan slidingExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - return (TResult)GetOrAddCore(factory, key, group, null, () => slidingExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the fifth parameter of the function delegate and the function delegate . - /// The type of the sixth parameter of the function delegate and the function delegate . - /// The type of the seventh parameter of the function delegate and the function delegate . - /// The type of the eighth parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The fifth parameter of the function delegate and the function delegate . - /// The sixth parameter of the function delegate and the function delegate . - /// The seventh parameter of the function delegate and the function delegate . - /// The eighth parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, Func> dependencyResolver) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, dependencyResolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the fifth parameter of the function delegate and the function delegate . - /// The type of the sixth parameter of the function delegate and the function delegate . - /// The type of the seventh parameter of the function delegate and the function delegate . - /// The type of the eighth parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The fifth parameter of the function delegate and the function delegate . - /// The sixth parameter of the function delegate and the function delegate . - /// The seventh parameter of the function delegate and the function delegate . - /// The eighth parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, Func> dependencyResolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - Validator.ThrowIfNull(dependencyResolver, nameof(dependencyResolver)); - var f1 = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - var f2 = FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - return (TResult)GetOrAddCore(f1, key, group, null, null, f2).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - return (TResult)GetOrAddCore(factory, key, group).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, DateTime absoluteExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, absoluteExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, DateTime absoluteExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - return (TResult)GetOrAddCore(factory, key, group, () => absoluteExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TimeSpan slidingExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, slidingExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TimeSpan slidingExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - return (TResult)GetOrAddCore(factory, key, group, null, () => slidingExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the fifth parameter of the function delegate and the function delegate . - /// The type of the sixth parameter of the function delegate and the function delegate . - /// The type of the seventh parameter of the function delegate and the function delegate . - /// The type of the eighth parameter of the function delegate and the function delegate . - /// The type of the ninth parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The fifth parameter of the function delegate and the function delegate . - /// The sixth parameter of the function delegate and the function delegate . - /// The seventh parameter of the function delegate and the function delegate . - /// The eighth parameter of the function delegate and the function delegate . - /// The ninth parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, Func> dependencyResolver) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, dependencyResolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the fifth parameter of the function delegate and the function delegate . - /// The type of the sixth parameter of the function delegate and the function delegate . - /// The type of the seventh parameter of the function delegate and the function delegate . - /// The type of the eighth parameter of the function delegate and the function delegate . - /// The type of the ninth parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The fifth parameter of the function delegate and the function delegate . - /// The sixth parameter of the function delegate and the function delegate . - /// The seventh parameter of the function delegate and the function delegate . - /// The eighth parameter of the function delegate and the function delegate . - /// The ninth parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, Func> dependencyResolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - Validator.ThrowIfNull(dependencyResolver, nameof(dependencyResolver)); - var f1 = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - var f2 = FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - return (TResult)GetOrAddCore(f1, key, group, null, null, f2).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the tenth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The tenth parameter of the function delegate . - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the tenth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The tenth parameter of the function delegate . - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - return (TResult)GetOrAddCore(factory, key, group).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the tenth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The tenth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, DateTime absoluteExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, absoluteExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the tenth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The tenth parameter of the function delegate . - /// The time at which the return value of expires and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, DateTime absoluteExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - return (TResult)GetOrAddCore(factory, key, group, () => absoluteExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the tenth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The tenth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, TimeSpan slidingExpiration) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, slidingExpiration); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the tenth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The tenth parameter of the function delegate . - /// The interval between the time the return value of was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, TimeSpan slidingExpiration) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - var factory = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - return (TResult)GetOrAddCore(factory, key, group, null, () => slidingExpiration).Value; - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the cache. - /// - /// The type of the first parameter of the function delegate . - /// The type of the second parameter of the function delegate . - /// The type of the third parameter of the function delegate . - /// The type of the fourth parameter of the function delegate . - /// The type of the fifth parameter of the function delegate . - /// The type of the sixth parameter of the function delegate . - /// The type of the seventh parameter of the function delegate . - /// The type of the eighth parameter of the function delegate . - /// The type of the ninth parameter of the function delegate . - /// The type of the tenth parameter of the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate . - /// The second parameter of the function delegate . - /// The third parameter of the function delegate . - /// The fourth parameter of the function delegate . - /// The fifth parameter of the function delegate . - /// The sixth parameter of the function delegate . - /// The seventh parameter of the function delegate . - /// The eighth parameter of the function delegate . - /// The ninth parameter of the function delegate . - /// The tenth parameter of the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value for the as returned by if the was not in the cache. - public TResult GetOrAdd(string key, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, Func> dependencyResolver) - { - return GetOrAdd(key, NoGroup, resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, dependencyResolver); - } - - /// - /// Adds a value to the cache by using the specified function delegate , if the does not already exist in the virtual of the cache. - /// - /// The type of the first parameter of the function delegate and the function delegate . - /// The type of the second parameter of the function delegate and the function delegate . - /// The type of the third parameter of the function delegate and the function delegate . - /// The type of the fourth parameter of the function delegate and the function delegate . - /// The type of the fifth parameter of the function delegate and the function delegate . - /// The type of the sixth parameter of the function delegate and the function delegate . - /// The type of the seventh parameter of the function delegate and the function delegate . - /// The type of the eighth parameter of the function delegate and the function delegate . - /// The type of the ninth parameter of the function delegate and the function delegate . - /// The type of the tenth parameter of the function delegate and the function delegate . - /// The type of the value in the cache. - /// The cache key used to identify the item. - /// The virtual group to associate the with. - /// The function delegate that is used to resolve a value for the . - /// The first parameter of the function delegate and the function delegate . - /// The second parameter of the function delegate and the function delegate . - /// The third parameter of the function delegate and the function delegate . - /// The fourth parameter of the function delegate and the function delegate . - /// The fifth parameter of the function delegate and the function delegate . - /// The sixth parameter of the function delegate and the function delegate . - /// The seventh parameter of the function delegate and the function delegate . - /// The eighth parameter of the function delegate and the function delegate . - /// The ninth parameter of the function delegate and the function delegate . - /// The tenth parameter of the function delegate and the function delegate . - /// The function delegate that is used to assign dependencies to the result of to the cache. When any dependency changes, the object becomes invalid and is removed from the cache. - /// The value for the specified and . This will either be the existing value if the is already in the virtual of the cache, or the new value for the as returned by if the was not in virtual of the cache. - public TResult GetOrAdd(string key, string group, Func resolver, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, Func> dependencyResolver) - { - Validator.ThrowIfNull(resolver, nameof(resolver)); - Validator.ThrowIfNull(dependencyResolver, nameof(dependencyResolver)); - var f1 = FuncFactory.Create(resolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - var f2 = FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - return (TResult)GetOrAddCore(f1, key, group, null, null, f2).Value; - } - - private Cache GetOrAddCore(FuncFactory valueFactory, string key, string group, Func absoluteExpiration = null, Func slidingExpiration = null, FuncFactory> dependenciesFactory = null) - where TTuple : Template - { - Validator.ThrowIfNull(key, nameof(key)); - if (slidingExpiration != null) - { - Validator.ThrowIfLowerThan(slidingExpiration().Ticks, TimeSpan.Zero.Ticks, nameof(slidingExpiration), "The specified sliding expiration cannot be less than TimeSpan.Zero."); - Validator.ThrowIfGreaterThan(slidingExpiration().Ticks, TimeSpan.FromDays(365).Ticks, nameof(slidingExpiration), "The specified sliding expiration cannot exceed one year."); - } - - long groupKey = GenerateGroupKey(key, group); - if (!TryGetCache(key, group, out var result)) - { - result = WrapCacheInThreadSafeDelegate(valueFactory, key, group, absoluteExpiration, slidingExpiration, dependenciesFactory).Value; - _innerCaches.TryAdd(groupKey, result); - } - return _innerCaches.GetOrAdd(groupKey, gk => WrapCacheInThreadSafeDelegate(valueFactory, key, group, absoluteExpiration, slidingExpiration, dependenciesFactory).Value); - } - - private Lazy WrapCacheInThreadSafeDelegate(FuncFactory valueFactory, string key, string group, Func absoluteExpiration = null, Func slidingExpiration = null, FuncFactory> dependenciesFactory = null) - where TTuple : Template - { - return new Lazy(() => - { - var cache = new Cache(key, valueFactory.ExecuteMethod(), group, dependenciesFactory?.ExecuteMethod(), absoluteExpiration?.Invoke() ?? DateTime.MaxValue, slidingExpiration?.Invoke() ?? TimeSpan.Zero); - cache.Expired += CacheExpired; - cache.StartDependencies(); - return cache; - }, LazyThreadSafetyMode.ExecutionAndPublication); - } - } -} \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/CacheCollection.Memoization.cs b/src/Cuemon.Runtime.Caching/CacheCollection.Memoization.cs deleted file mode 100644 index 470c6e5f3..000000000 --- a/src/Cuemon.Runtime.Caching/CacheCollection.Memoization.cs +++ /dev/null @@ -1,929 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using Cuemon.Reflection; - -namespace Cuemon.Runtime.Caching -{ - public sealed partial class CacheCollection - { - private const string MemoizationGroup = "Memoization"; - private const long NullHashCode = 854726591; - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The time at which the memoized function delegate expires and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, DateTime absoluteExpiration) - { - return MemoizeCore(method, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The interval between the time the memoized function delegate was last accessed and the time at which that memoization expires. If this value is the equivalent of 20 minutes, the memoization expires and is removed from the cache 20 minutes after it was last accessed. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, TimeSpan slidingExpiration) - { - return MemoizeCore(method, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The function delegate that is used to assign dependencies to the memoized . When any dependency changes, the object becomes invalid and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, Func> dependencyResolver) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, dependencyResolver); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The time at which the memoized function delegate expires and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, DateTime absoluteExpiration) - { - return MemoizeCore(method, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The interval between the time the memoized function delegate was last accessed and the time at which that memoization expires. If this value is the equivalent of 20 minutes, the memoization expires and is removed from the cache 20 minutes after it was last accessed. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, TimeSpan slidingExpiration) - { - return MemoizeCore(method, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The function delegate that is used to assign dependencies to the memoized . When any dependency changes, the object becomes invalid and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, Func> dependencyResolver) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, dependencyResolver); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The time at which the memoized function delegate expires and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, DateTime absoluteExpiration) - { - return MemoizeCore(method, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The interval between the time the memoized function delegate was last accessed and the time at which that memoization expires. If this value is the equivalent of 20 minutes, the memoization expires and is removed from the cache 20 minutes after it was last accessed. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, TimeSpan slidingExpiration) - { - return MemoizeCore(method, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The function delegate that is used to assign dependencies to the memoized . When any dependency changes, the object becomes invalid and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, Func> dependencyResolver) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, dependencyResolver); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The time at which the memoized function delegate expires and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, DateTime absoluteExpiration) - { - return MemoizeCore(method, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate .> - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The interval between the time the memoized function delegate was last accessed and the time at which that memoization expires. If this value is the equivalent of 20 minutes, the memoization expires and is removed from the cache 20 minutes after it was last accessed. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, TimeSpan slidingExpiration) - { - return MemoizeCore(method, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The function delegate that is used to assign dependencies to the memoized . When any dependency changes, the object becomes invalid and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, Func> dependencyResolver) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, dependencyResolver); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The time at which the memoized function delegate expires and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, DateTime absoluteExpiration) - { - return MemoizeCore(method, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The interval between the time the memoized function delegate was last accessed and the time at which that memoization expires. If this value is the equivalent of 20 minutes, the memoization expires and is removed from the cache 20 minutes after it was last accessed. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, TimeSpan slidingExpiration) - { - return MemoizeCore(method, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The function delegate that is used to assign dependencies to the memoized . When any dependency changes, the object becomes invalid and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, Func> dependencyResolver) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, dependencyResolver); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The time at which the memoized function delegate expires and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, DateTime absoluteExpiration) - { - return MemoizeCore(method, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The interval between the time the memoized function delegate was last accessed and the time at which that memoization expires. If this value is the equivalent of 20 minutes, the memoization expires and is removed from the cache 20 minutes after it was last accessed. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, TimeSpan slidingExpiration) - { - return MemoizeCore(method, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The function delegate that is used to assign dependencies to the memoized . When any dependency changes, the object becomes invalid and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, Func> dependencyResolver) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, dependencyResolver); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The time at which the memoized function delegate expires and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, DateTime absoluteExpiration) - { - return MemoizeCore(method, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The interval between the time the memoized function delegate was last accessed and the time at which that memoization expires. If this value is the equivalent of 20 minutes, the memoization expires and is removed from the cache 20 minutes after it was last accessed. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, TimeSpan slidingExpiration) - { - return MemoizeCore(method, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The function delegate that is used to assign dependencies to the memoized . When any dependency changes, the object becomes invalid and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, Func> dependencyResolver) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, dependencyResolver); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The time at which the memoized function delegate expires and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, DateTime absoluteExpiration) - { - return MemoizeCore(method, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The interval between the time the memoized function delegate was last accessed and the time at which that memoization expires. If this value is the equivalent of 20 minutes, the memoization expires and is removed from the cache 20 minutes after it was last accessed. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, TimeSpan slidingExpiration) - { - return MemoizeCore(method, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The function delegate that is used to assign dependencies to the memoized . When any dependency changes, the object becomes invalid and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, Func> dependencyResolver) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, dependencyResolver); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The time at which the memoized function delegate expires and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, DateTime absoluteExpiration) - { - return MemoizeCore(method, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The interval between the time the memoized function delegate was last accessed and the time at which that memoization expires. If this value is the equivalent of 20 minutes, the memoization expires and is removed from the cache 20 minutes after it was last accessed. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, TimeSpan slidingExpiration) - { - return MemoizeCore(method, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The function delegate that is used to assign dependencies to the memoized . When any dependency changes, the object becomes invalid and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, Func> dependencyResolver) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, dependencyResolver); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the ninth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the ninth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The time at which the memoized function delegate expires and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, DateTime absoluteExpiration) - { - return MemoizeCore(method, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the ninth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The interval between the time the memoized function delegate was last accessed and the time at which that memoization expires. If this value is the equivalent of 20 minutes, the memoization expires and is removed from the cache 20 minutes after it was last accessed. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, TimeSpan slidingExpiration) - { - return MemoizeCore(method, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the ninth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The function delegate that is used to assign dependencies to the memoized . When any dependency changes, the object becomes invalid and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, Func> dependencyResolver) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, dependencyResolver); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the ninth parameter of the delegate . - /// The type of the tenth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the ninth parameter of the delegate . - /// The type of the tenth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The time at which the memoized function delegate expires and is removed from the cache. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, DateTime absoluteExpiration) - { - return MemoizeCore(method, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the ninth parameter of the delegate . - /// The type of the tenth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// The interval between the time the memoized function delegate was last accessed and the time at which that memoization expires. If this value is the equivalent of 20 minutes, the memoization expires and is removed from the cache 20 minutes after it was last accessed. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, TimeSpan slidingExpiration) - { - return MemoizeCore(method, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Memoizes the specified in the cache for fast access. - /// - /// The type of the first parameter of the delegate . - /// The type of the second parameter of the delegate . - /// The type of the third parameter of the delegate . - /// The type of the fourth parameter of the delegate . - /// The type of the fifth parameter of the delegate . - /// The type of the sixth parameter of the delegate . - /// The type of the seventh parameter of the delegate . - /// The type of the eighth parameter of the delegate . - /// The type of the ninth parameter of the delegate . - /// The type of the tenth parameter of the delegate . - /// The type of the return value of the function delegate . - /// The function delegate that is invoked once and then stored in cache for fast access. - /// Establishes one or more relations to this memoized function delegate. - /// A memoized function delegate that is otherwise equivalent to . - public Func Memoize(Func method, Func> dependencyResolver) - { - return MemoizeCore(method, DateTime.MaxValue, TimeSpan.Zero, dependencyResolver); - } - - private Func MemoizeCore(Func method, DateTime absoluteExpiration, TimeSpan slidingExpiration, Func> dependencyResolver) - { - return delegate - { - string key = CalculateCompositeKey(method); - if (!TryGetValue(key, MemoizationGroup, out TResult result)) - { - var f1 = FuncFactory.Create(method); - var f2 = dependencyResolver == null ? null : FuncFactory.Create(dependencyResolver); - result = (TResult)GetOrAddCore(f1, key, MemoizationGroup, () => absoluteExpiration, () => slidingExpiration, f2).Value; - } - return result; - }; - } - - private Func MemoizeCore(Func method, DateTime absoluteExpiration, TimeSpan slidingExpiration, Func> dependencyResolver) - { - return delegate (T arg) - { - string key = CalculateCompositeKey(method, arg); - if (!TryGetValue(key, MemoizationGroup, out TResult result)) - { - var f1 = FuncFactory.Create(method, arg); - var f2 = dependencyResolver == null ? null : FuncFactory.Create(dependencyResolver, arg); - result = (TResult)GetOrAddCore(f1, key, MemoizationGroup, () => absoluteExpiration, () => slidingExpiration, f2).Value; - } - return result; - }; - } - - private Func MemoizeCore(Func method, DateTime absoluteExpiration, TimeSpan slidingExpiration, Func> dependencyResolver) - { - return delegate (T1 arg1, T2 arg2) - { - string key = CalculateCompositeKey(method, arg1, arg2); - if (!TryGetValue(key, MemoizationGroup, out TResult result)) - { - var f1 = FuncFactory.Create(method, arg1, arg2); - var f2 = dependencyResolver == null ? null : FuncFactory.Create(dependencyResolver, arg1, arg2); - result = (TResult)GetOrAddCore(f1, key, MemoizationGroup, () => absoluteExpiration, () => slidingExpiration, f2).Value; - } - return result; - }; - } - - private Func MemoizeCore(Func method, DateTime absoluteExpiration, TimeSpan slidingExpiration, Func> dependencyResolver) - { - return delegate (T1 arg1, T2 arg2, T3 arg3) - { - string key = CalculateCompositeKey(method, arg1, arg2, arg3); - if (!TryGetValue(key, MemoizationGroup, out TResult result)) - { - var f1 = FuncFactory.Create(method, arg1, arg2, arg3); - var f2 = dependencyResolver == null ? null : FuncFactory.Create(dependencyResolver, arg1, arg2, arg3); - result = (TResult)GetOrAddCore(f1, key, MemoizationGroup, () => absoluteExpiration, () => slidingExpiration, f2).Value; - } - return result; - }; - } - - private Func MemoizeCore(Func method, DateTime absoluteExpiration, TimeSpan slidingExpiration, Func> dependencyResolver) - { - return delegate (T1 arg1, T2 arg2, T3 arg3, T4 arg4) - { - string key = CalculateCompositeKey(method, arg1, arg2, arg3, arg4); - if (!TryGetValue(key, MemoizationGroup, out TResult result)) - { - var f1 = FuncFactory.Create(method, arg1, arg2, arg3, arg4); - var f2 = dependencyResolver == null ? null : FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4); - result = (TResult)GetOrAddCore(f1, key, MemoizationGroup, () => absoluteExpiration, () => slidingExpiration, f2).Value; - } - return result; - }; - } - - private Func MemoizeCore(Func method, DateTime absoluteExpiration, TimeSpan slidingExpiration, Func> dependencyResolver) - { - return delegate (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) - { - string key = CalculateCompositeKey(method, arg1, arg2, arg3, arg4, arg5); - if (!TryGetValue(key, MemoizationGroup, out TResult result)) - { - var f1 = FuncFactory.Create(method, arg1, arg2, arg3, arg4, arg5); - var f2 = dependencyResolver == null ? null : FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5); - result = (TResult)GetOrAddCore(f1, key, MemoizationGroup, () => absoluteExpiration, () => slidingExpiration, f2).Value; - } - return result; - }; - } - - private Func MemoizeCore(Func method, DateTime absoluteExpiration, TimeSpan slidingExpiration, Func> dependencyResolver) - { - return delegate (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) - { - string key = CalculateCompositeKey(method, arg1, arg2, arg3, arg4, arg5, arg6); - if (!TryGetValue(key, MemoizationGroup, out TResult result)) - { - var f1 = FuncFactory.Create(method, arg1, arg2, arg3, arg4, arg5, arg6); - var f2 = dependencyResolver == null ? null : FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5, arg6); - result = (TResult)GetOrAddCore(f1, key, MemoizationGroup, () => absoluteExpiration, () => slidingExpiration, f2).Value; - } - return result; - }; - } - - private Func MemoizeCore(Func method, DateTime absoluteExpiration, TimeSpan slidingExpiration, Func> dependencyResolver) - { - return delegate (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) - { - string key = CalculateCompositeKey(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - if (!TryGetValue(key, MemoizationGroup, out TResult result)) - { - var f1 = FuncFactory.Create(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - var f2 = dependencyResolver == null ? null : FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - result = (TResult)GetOrAddCore(f1, key, MemoizationGroup, () => absoluteExpiration, () => slidingExpiration, f2).Value; - } - return result; - }; - } - - private Func MemoizeCore(Func method, DateTime absoluteExpiration, TimeSpan slidingExpiration, Func> dependencyResolver) - { - return delegate (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) - { - string key = CalculateCompositeKey(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - if (!TryGetValue(key, MemoizationGroup, out TResult result)) - { - var f1 = FuncFactory.Create(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - var f2 = dependencyResolver == null ? null : FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - result = (TResult)GetOrAddCore(f1, key, MemoizationGroup, () => absoluteExpiration, () => slidingExpiration, f2).Value; - } - return result; - }; - } - - private Func MemoizeCore(Func method, DateTime absoluteExpiration, TimeSpan slidingExpiration, Func> dependencyResolver) - { - return delegate (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) - { - string key = CalculateCompositeKey(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - if (!TryGetValue(key, MemoizationGroup, out TResult result)) - { - var f1 = FuncFactory.Create(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - var f2 = dependencyResolver == null ? null : FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - result = (TResult)GetOrAddCore(f1, key, MemoizationGroup, () => absoluteExpiration, () => slidingExpiration, f2).Value; - } - return result; - }; - } - - private Func MemoizeCore(Func method, DateTime absoluteExpiration, TimeSpan slidingExpiration, Func> dependencyResolver) - { - return delegate (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) - { - string key = CalculateCompositeKey(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - if (!TryGetValue(key, MemoizationGroup, out TResult result)) - { - var f1 = FuncFactory.Create(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - var f2 = dependencyResolver == null ? null : FuncFactory.Create(dependencyResolver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - result = (TResult)GetOrAddCore(f1, key, MemoizationGroup, () => absoluteExpiration, () => slidingExpiration, f2).Value; - } - return result; - }; - } - - private static string CalculateCompositeKey(Delegate del, params object[] args) - { - int result = del == null || del.GetMethodInfo() == null ? NullHashCode.GetHashCode() : MethodDescriptor.Create(del.GetMethodInfo()).ToString().GetHashCode(); - for (int i = 0; i < args.Length; i++) - { - object current = args[i] ?? NullHashCode; - byte[] bytes = current as byte[]; - result ^= bytes == null ? current.GetHashCode() : Generate.HashCode32(bytes.Cast()); - } - return result.ToString(CultureInfo.InvariantCulture); - } - } -} \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/CacheCollection.cs b/src/Cuemon.Runtime.Caching/CacheCollection.cs deleted file mode 100644 index 4c3870acd..000000000 --- a/src/Cuemon.Runtime.Caching/CacheCollection.cs +++ /dev/null @@ -1,599 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading; -using Cuemon.Collections.Generic; - -namespace Cuemon.Runtime.Caching -{ - /// - /// Implements a cache for an application. This class cannot be inherited. - /// - public sealed partial class CacheCollection : IEnumerable> - { - private static readonly CacheCollection Singleton = new CacheCollection(); - private readonly ConcurrentDictionary _innerCaches = new ConcurrentDictionary(); - private const string NoGroup = null; - - internal static CacheCollection Cache - { - get { return Singleton; } - } - - #region Constructors - private CacheCollection() - { - EnableExpirationTimer = true; - ExpirationTimer = new Timer(ExpirationTimerInvoking, null, TimeSpan.FromSeconds(5), TimeSpan.FromMinutes(20)); - } - #endregion - - #region Properties - /// - /// Gets or sets a value indicating whether a timer regularly should clean up expired cache items. - /// - /// true if a timer regularly should clean up expired cache items; otherwise, false. - public bool EnableExpirationTimer { get; set; } - - /// - /// Gets the cached item with the specified . - /// - /// The cached item matching the specified . - public object this[string key] - { - get - { - return this[key, NoGroup]; - } - } - - /// - /// Gets the cached item with the specified and . - /// - /// The cached item matching the specified and . - public object this[string key, string group] - { - get - { - return Get(key, group); - } - } - - private Timer ExpirationTimer { get; } - #endregion - - #region Methods - /// - /// Retrieves the specified item from the . - /// - /// The type of the item in the . - /// The identifier of the cache item to retrieve. - /// The retrieved cache item, or the default value of the type parameter T if the key is not found. - public T Get(string key) - { - return Get(key, NoGroup); - } - - /// - /// Retrieves the specified item from the associated group of the . - /// - /// The type of the item in the . - /// The identifier of the cache item to retrieve. - /// The associated group of the cache item to retrieve. - /// The retrieved cache item, or the default value of the type parameter T if the key is not found. - public T Get(string key, string group) - { - if (key == null) { throw new ArgumentNullException(nameof(key)); } - TryGetValue(key, group, out T result); - return result; - } - - /// - /// Updates the specified item from the associated group of the . - /// - /// The type of the item in the . - /// The identifier of the cache item to retrieve and update with . - /// The associated group of the cache item to retrieve and update with . - /// The value to apply to the cached item. - internal void Set(string key, string group, T value) - { - if (key == null) { throw new ArgumentNullException(nameof(key)); } - if (TryGetCache(key, group, out var cache)) - { - cache.Value = value; - cache.Refresh(); - } - } - - /// - /// Gets the UTC date time value from when this item was added to the . - /// - /// The key of the value to get. - /// When this method returns, contains the UTC date time value from when this item, with the specified key, was added; otherwise, if no item could be resolved or the item has expired, . This parameter is passed uninitialized. - /// - /// true if the parameter contains an element with the specified key, and the element has not expired; otherwise, false. - /// - /// - /// is null. - /// - public bool TryGetAdded(string key, out DateTime value) - { - return TryGetAdded(key, NoGroup, out value); - } - - /// - /// Gets the UTC date time value from when this item was added to the . - /// - /// The key of the value to get. - /// The group of the value to get. - /// When this method returns, contains the UTC date time value from when this item, with the specified key, was added; otherwise, if no item could be resolved or the item has expired, . This parameter is passed uninitialized. - /// - /// true if the parameter contains an element with the specified key, and the element has not expired; otherwise, false. - /// - /// - /// is null. - /// - public bool TryGetAdded(string key, string group, out DateTime value) - { - if (key == null) { throw new ArgumentNullException(nameof(key)); } - if (TryGetCache(key, group, out var cache)) - { - value = cache.Created; - return true; - } - value = DateTime.MinValue; - return false; - } - - private static long GenerateGroupKey(string key, string group) - { - return Generate.HashCode64(group == NoGroup ? key : string.Concat(key, group)); - } - - /// - /// Adds the specified and to the cache. - /// - /// The cache key used to identify the item. - /// The object to be inserted in the cache. - /// - /// is null. - /// - /// - /// This method will not throw an in case of an existing cache item whose key matches the key parameter. - /// - public void Add(string key, object value) - { - Add(key, value, NoGroup); - } - - /// - /// Adds the specified and to the cache.up. - /// - /// The cache key used to identify the item. - /// The object to be inserted in the cache. - /// The group to associate the with. - /// - /// is null. - /// - /// - /// This method will not throw an in case of an existing cache item whose key matches the key parameter. - /// - public void Add(string key, object value, string group) - { - AddCore(key, value, group, DateTime.MaxValue, TimeSpan.Zero, null); - } - - /// - /// Adds the specified and to the cache. - /// - /// The cache key used to identify the item. - /// The object to be inserted in the cache. - /// The time at which the added object expires and is removed from the cache. - /// - /// is null. - /// - /// - /// This method will not throw an in case of an existing cache item whose key matches the key parameter. - /// - public void Add(string key, object value, DateTime absoluteExpiration) - { - Add(key, value, NoGroup, absoluteExpiration); - } - - /// - /// Adds the specified and to the cache. - /// - /// The cache key used to identify the item. - /// The object to be inserted in the cache. - /// The group to associate the with. - /// The time at which the added object expires and is removed from the cache. - /// - /// is null. - /// - /// - /// This method will not throw an in case of an existing cache item whose key matches the key parameter. - /// - public void Add(string key, object value, string group, DateTime absoluteExpiration) - { - AddCore(key, value, group, absoluteExpiration, TimeSpan.Zero, null); - } - - /// - /// Adds the specified and to the cache. - /// - /// The cache key used to identify the item. - /// The object to be inserted in the cache. - /// The interval between the time the added object was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it is last accessed. - /// - /// is null. - /// - /// - /// This method will not throw an in case of an existing cache item whose key matches the key parameter. - /// - public void Add(string key, object value, TimeSpan slidingExpiration) - { - Add(key, value, NoGroup, slidingExpiration); - } - - /// - /// Adds the specified and to the cache. - /// - /// The cache key used to identify the item. - /// The object to be inserted in the cache. - /// The group to associate the with. - /// The interval between the time the added object was last accessed and the time at which that object expires. If this value is the equivalent of 20 minutes, the object expires and is removed from the cache 20 minutes after it was last accessed. - /// - /// is null. - /// - /// - /// This method will not throw an in case of an existing cache item whose key matches the key parameter. - /// - public void Add(string key, object value, string group, TimeSpan slidingExpiration) - { - AddCore(key, value, group, DateTime.MaxValue, slidingExpiration, null); - } - - /// - /// Adds the specified and to the cache. - /// - /// The cache key used to identify the item. - /// The object to be inserted in the cache. - /// The dependencies for the . When any dependency changes, the becomes invalid and is removed from the cache. - /// - /// is null. - /// - /// - /// This method will not throw an in case of an existing cache item whose key matches the key parameter. - /// - public void Add(string key, object value, params IDependency[] dependencies) - { - Add(key, value, NoGroup, dependencies); - } - - /// - /// Adds the specified and to the cache. - /// - /// The cache key used to identify the item. - /// The object to be inserted in the cache. - /// The dependencies for the . When any dependency changes, the becomes invalid and is removed from the cache. - /// - /// is null. - /// - /// - /// This method will not throw an in case of an existing cache item whose key matches the key parameter. - /// - public void Add(string key, object value, IEnumerable dependencies) - { - Add(key, value, NoGroup, dependencies); - } - - /// - /// Adds the specified and to the cache. - /// - /// The cache key used to identify the item. - /// The object to be inserted in the cache. - /// The group to associate the with. - /// The dependencies for the . When any dependency changes, the becomes invalid and is removed from the cache. - /// - /// is null. - /// - /// - /// This method will not throw an in case of an existing cache item whose key matches the key parameter. - /// - public void Add(string key, object value, string group, params IDependency[] dependencies) - { - Add(key, value, group, Arguments.ToEnumerableOf(dependencies)); - } - - /// - /// Adds the specified and to the cache. - /// - /// The cache key used to identify the item. - /// The object to be inserted in the cache. - /// The group to associate the with. - /// The dependencies for the . When any dependency changes, the becomes invalid and is removed from the cache. - /// - /// is null. - /// - /// - /// This method will not throw an in case of an existing cache item whose key matches the key parameter. - /// - public void Add(string key, object value, string group, IEnumerable dependencies) - { - AddCore(key, value, group, DateTime.MaxValue, TimeSpan.Zero, dependencies); - } - - private void AddCore(string key, object value, string group, DateTime absoluteExpiration, TimeSpan slidingExpiration, IEnumerable dependencies) - { - var f1 = FuncFactory.Create(() => value); - var f2 = FuncFactory.Create(() => dependencies); - GetOrAddCore(f1, key, group, () => absoluteExpiration, () => slidingExpiration, f2); - } - - private void ExpirationTimerInvoking(object o) - { - HandleExpiration(); - } - - private void RemoveExpired(string key, string group) - { - Remove(key, group); - } - - private void HandleExpiration() - { - if (!EnableExpirationTimer) { return; } - DateTime current = DateTime.UtcNow; - List snapshot = new List(_innerCaches.Values); - if (snapshot.Count > 0) - { - foreach (Cache cache in snapshot) - { - if (cache == null) { continue; } - if (cache.CanExpire && cache.HasExpired(current)) { RemoveExpired(cache.Key, cache.Group); } - } - } - } - - private void CacheExpired(object sender, CacheEventArgs e) - { - e.Cache.Expired -= CacheExpired; - } - - private IList GetCaches(string group) - { - var current = DateTime.UtcNow; - var groupCaches = new List(); - var snapshot = new List(_innerCaches.Values); - foreach (Cache cache in snapshot) - { - if (cache == null) { continue; } // this can happen if a cache has been removed - if (cache.CanExpire && cache.HasExpired(current)) { continue; } - if (group == NoGroup) - { - groupCaches.Add(cache); // return all - } - else if (cache.Group == group) - { - groupCaches.Add(cache); // return filtered by group - } - } - return groupCaches; - } - - /// - /// Determines whether the contains the specified key. - /// - /// The key to locate in the . - /// - /// true if the contains an element with the specified key; otherwise, false. - /// - /// - /// is null. - /// - public bool ContainsKey(string key) - { - return ContainsKey(key, NoGroup); - } - - /// - /// Determines whether the contains the specified key. - /// - /// The key to locate in the . - /// The associated group of the key to locate. - /// - /// true if the contains an element with the specified key; otherwise, false. - /// - /// - /// is null. - /// - public bool ContainsKey(string key, string group) - { - if (key == null) { throw new ArgumentNullException(nameof(key)); } - return TryGetCache(key, group, out _); - } - - /// - /// Removes all keys and values from the . - /// - public void Clear() - { - Clear(NoGroup); - } - - /// - /// Removes all keys and values matching the specified group from the . - /// - public void Clear(string group) - { - if (group == NoGroup) - { - _innerCaches.Clear(); - } - else - { - IList groupCaches = GetCaches(group); - foreach (var cache in groupCaches) - { - Remove(cache.Key, cache.Group); - } - } - } - - /// - /// Gets the number of elements contained in the . - /// - /// - /// - /// The number of elements contained in the . - /// - public int Count() - { - return Count(NoGroup); - } - - /// - /// Gets the number of elements contained in the specified group of the . - /// - /// The associated group to filter the count by. - /// - /// The number of elements contained in the . - /// - /// - public int Count(string group) - { - int count = GetCaches(group).Count; - return count; - } - - /// - /// Gets the value associated with the specified key. - /// - /// The type of the item in the . - /// The key of the value to get. - /// When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. - /// - /// true if the contains an element with the specified key; otherwise, false. - /// - /// - /// is null. - /// - public bool TryGetValue(string key, out T value) - { - return TryGetValue(key, NoGroup, out value); - } - - /// - /// Gets the value associated with the specified key and group. - /// - /// The type of the item in the . - /// The key of the value to get. - /// The group of the value to get. - /// When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. - /// - /// true if the contains an element with the specified key; otherwise, false. - /// - /// - /// is null. - /// - public bool TryGetValue(string key, string group, out T value) - { - if (key == null) { throw new ArgumentNullException(nameof(key)); } - - if (TryGetCache(key, group, out var cache)) - { - value = (T)cache.Value; - return true; - } - value = default; - return false; - } - - private bool TryGetCache(string key, string group, out Cache cache) - { - DateTime current = DateTime.UtcNow; - long groupKey = GenerateGroupKey(key, group); - if (_innerCaches.TryGetValue(groupKey, out cache)) - { - bool hasCacheExpired = cache.HasExpired(current); - if (cache.CanExpire && hasCacheExpired) - { - RemoveExpired(key, group); - return false; - } - if (cache.CanExpire && !hasCacheExpired) { cache.Refresh(); } - return true; - } - return false; - } - - /// - /// Removes the value with the specified key from the . - /// - /// The key of the element to remove. - /// - /// true if the element is successfully found and removed; otherwise, false. This method returns false if is not found in the . - /// - /// - /// is null. - /// - public bool Remove(string key) - { - return Remove(key, NoGroup); - } - - /// - /// Removes the value with the specified key from the associated specified group of the . - /// - /// The key of the element to remove. - /// The associated group to the key of the element to remove. - /// - /// true if the element is successfully found and removed; otherwise, false. This method returns false if combined with is not found in the . - /// - /// - /// is null. - /// - public bool Remove(string key, string group) - { - if (key == null) { throw new ArgumentNullException(nameof(key)); } - long groupKey = GenerateGroupKey(key, group); - return _innerCaches.TryRemove(groupKey, out _); - } - - private IEnumerable> CreateImpostor() - { - foreach (var keyValuePair in _innerCaches) - { - if (keyValuePair.Value != null) - { - yield return new KeyValuePair(keyValuePair.Key, keyValuePair.Value.Value); - } - } - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - - /// - /// Retrieves an enumerator that iterates through the key settings and their values contained in the cache. - /// - /// A that can be used to iterate through the collection. - /// All keys are hashed internally and will not provide useful information. - public IEnumerator> GetEnumerator() - { - List> impostor = new List>(CreateImpostor()); - return impostor.GetEnumerator(); - } - #endregion - } -} \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/CacheEntry.cs b/src/Cuemon.Runtime.Caching/CacheEntry.cs new file mode 100644 index 000000000..53699da16 --- /dev/null +++ b/src/Cuemon.Runtime.Caching/CacheEntry.cs @@ -0,0 +1,168 @@ +using System; + +namespace Cuemon.Runtime.Caching +{ + /// + /// Represents an individual cache entry in the cache. + /// + public class CacheEntry + { + /// + /// Represents a cache with a global scope, eg. no namespace. + /// + public const string NoScope = null; + + /// + /// Initializes a new instance of the class. + /// + /// The unique identifier of the cache. + /// The stored value of the cache. + /// The optional namespace that provides a scope to the cache. + /// + /// cannot be null. + /// + public CacheEntry(string key, object value, string ns = NoScope) + { + Validator.ThrowIfNull(key, nameof(key)); + var timestamp = DateTime.UtcNow; + Key = key; + Value = value; + Namespace = ns; + Inserted = timestamp; + Accessed = timestamp; + } + + /// + /// Occurs when a object with an associated has expired. + /// + public event EventHandler Expired; + + /// + /// Gets the unique identifier of this . + /// + /// The unique identifier of this . + public string Key { get; } + + /// + /// Gets the stored value of this . + /// + /// The stored value of this . + public object Value { get; set; } + + /// + /// Gets the optional namespace that provides a scope to this . + /// + /// The optional namespace that provides a scope to this . + public string Namespace { get; } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + return Generate.ObjectPortrayal(this, o => o.BypassOverrideCheck = true); + } + + /// + /// Gets the cache invalidation of this . + /// + /// The cache invalidation of this . + public CacheInvalidation Invalidation { get; private set; } + + /// + /// Gets the UTC date time value from when this was inserted. + /// + /// The UTC date time value from when this was inserted. + public DateTime Inserted { get; } + + /// + /// Gets the UTC date time value from when this was last accessed. + /// + /// The UTC date time value from when this was last accessed. + public DateTime Accessed { get; private set; } + + /// + /// Gets a value indicating whether this can expire. + /// + /// + /// true if this can expire; otherwise, false. + /// + public bool CanExpire => Invalidation.UseAbsoluteExpiration || Invalidation.UseSlidingExpiration || Invalidation.UseDependency; + + internal CacheEntry SetInvalidation(CacheInvalidation invalidation) + { + Validator.ThrowIfNull(invalidation, nameof(invalidation)); + Invalidation = invalidation; + return this; + } + + /// + /// Determines whether the specified time resolves this as expired. + /// + /// The date and time to evaluate against. + /// + /// true if the specified time resolves this as expired; otherwise, false. + /// + public bool HasExpired(DateTime time) + { + if (!CanExpire) { return false; } + if (Invalidation.UseAbsoluteExpiration) + { + if (time >= Invalidation.AbsoluteExpiration) { return true; } + } + else if (Invalidation.UseSlidingExpiration) + { + var currentPeriod = (time - Accessed); + if (currentPeriod >= Invalidation.SlidingExpiration) { return true; } + } + else if (Invalidation.UseDependency) + { + foreach (var dependency in Invalidation.Dependencies) + { + if (dependency.HasChanged) { return true; } + } + } + return false; + } + + internal void Refresh() + { + Accessed = DateTime.UtcNow; + } + + internal CacheEntry StartDependencies() + { + if (Invalidation.UseDependency) + { + foreach (var dependency in Invalidation.Dependencies) + { + dependency.DependencyChanged += ProcessDependencyChanged; + dependency.Start(); + } + } + return this; + } + + private void ProcessDependencyChanged(object sender, DependencyEventArgs e) + { + if (Invalidation.UseDependency) + { + OnExpiredRaised(new CacheEntryEventArgs(this)); + foreach (var dependency in Invalidation.Dependencies) + { + dependency.DependencyChanged -= ProcessDependencyChanged; + } + } + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnExpiredRaised(CacheEntryEventArgs e) + { + Expired?.Invoke(this, e); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/CacheEventArgs.cs b/src/Cuemon.Runtime.Caching/CacheEntryEventArgs.cs similarity index 60% rename from src/Cuemon.Runtime.Caching/CacheEventArgs.cs rename to src/Cuemon.Runtime.Caching/CacheEntryEventArgs.cs index 0d45416c7..8eb0081c7 100644 --- a/src/Cuemon.Runtime.Caching/CacheEventArgs.cs +++ b/src/Cuemon.Runtime.Caching/CacheEntryEventArgs.cs @@ -5,13 +5,13 @@ namespace Cuemon.Runtime.Caching /// /// Provides data for cache related operations. This class cannot be inherited. /// - public sealed class CacheEventArgs : EventArgs + public sealed class CacheEntryEventArgs : EventArgs { - internal CacheEventArgs(Cache cache) + internal CacheEntryEventArgs(CacheEntry cache) { Cache = cache; } - internal Cache Cache { get; private set; } + internal CacheEntry Cache { get; } } } \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/CacheInvalidation.cs b/src/Cuemon.Runtime.Caching/CacheInvalidation.cs new file mode 100644 index 000000000..bb3d52cb3 --- /dev/null +++ b/src/Cuemon.Runtime.Caching/CacheInvalidation.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Cuemon.Runtime.Caching +{ + /// + /// Represents a set of eviction and expiration details for a specific cache entry. + /// + public class CacheInvalidation + { + /// + /// Initializes a new instance of the class. + /// + /// The absolute expiration date time value from when the cached value becomes invalid and is removed from the cache. + public CacheInvalidation(DateTime absoluteExpiration) + { + AbsoluteExpiration = absoluteExpiration.ToUniversalTime(); + } + + /// + /// Initializes a new instance of the class. + /// + /// A sequence of implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached value becomes invalid and is removed from the cache. + public CacheInvalidation(IEnumerable dependencies) + { + Dependencies = dependencies ?? Enumerable.Empty(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The sliding expiration time from when the cached value becomes invalid and is removed from the cache. + public CacheInvalidation(TimeSpan slidingExpiration) + { + Validator.ThrowIfLowerThanOrEqual(slidingExpiration.Ticks, TimeSpan.Zero.Ticks, nameof(slidingExpiration), "The specified sliding expiration cannot be less than or equal to TimeSpan.Zero."); + Validator.ThrowIfGreaterThan(slidingExpiration.Ticks, TimeSpan.FromDays(365).Ticks, nameof(slidingExpiration), "The specified sliding expiration cannot exceed one year."); + SlidingExpiration = slidingExpiration; + } + + /// + /// Gets a sequence of objects implementing the interface assigned to a . + /// + /// A sequence of objects implementing the interface assigned to a . + public IEnumerable Dependencies { get; } + + /// + /// Gets the UTC absolute expiration date time value of a . + /// + /// The UTC absolute expiration date time value of a . + public DateTime? AbsoluteExpiration { get; } + + /// + /// Gets the sliding expiration time of a . + /// + /// The sliding expiration time of a . + public TimeSpan? SlidingExpiration { get; } + + /// + /// Gets a value indicating whether a should use property for cache invalidation. + /// + /// + /// true if a should use property for cache invalidation; otherwise, false. + /// + public bool UseAbsoluteExpiration => AbsoluteExpiration.HasValue; + + /// + /// Gets a value indicating whether a should use property for cache invalidation. + /// + /// + /// true if a should use property for cache invalidation; otherwise, false. + /// + public bool UseSlidingExpiration => SlidingExpiration.HasValue; + + /// + /// Gets a value indicating whether this is relying on an implementation for cache invalidation. + /// + /// true if a is relying on an implementation for cache invalidation; otherwise, false. + public bool UseDependency => Dependencies != null && Dependencies.Any(); + } +} \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/CachingManager.cs b/src/Cuemon.Runtime.Caching/CachingManager.cs index 8b9abb06a..2b750c362 100644 --- a/src/Cuemon.Runtime.Caching/CachingManager.cs +++ b/src/Cuemon.Runtime.Caching/CachingManager.cs @@ -1,17 +1,19 @@ -namespace Cuemon.Runtime.Caching +using System; +using System.Threading; + +namespace Cuemon.Runtime.Caching { /// /// Provides access to caching in an application. /// public static class CachingManager { + private static readonly Lazy Singleton = new Lazy(LazyThreadSafetyMode.ExecutionAndPublication); + /// - /// Gets a collection of cached objects for the current application domain. + /// Gets a singleton instance of that is an in-memory cache for an application. /// - /// A collection of cached objects for the current application domain. - public static CacheCollection Cache - { - get { return CacheCollection.Cache; } - } + /// A singleton instance of that is an in-memory cache for an application. + public static SlimMemoryCache Cache => Singleton.Value; } } \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj b/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj index 984e900bf..2a03fd0a3 100644 --- a/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj +++ b/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj @@ -14,6 +14,7 @@ + \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/ICacheEnumerable.cs b/src/Cuemon.Runtime.Caching/ICacheEnumerable.cs new file mode 100644 index 000000000..0664931c0 --- /dev/null +++ b/src/Cuemon.Runtime.Caching/ICacheEnumerable.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; + +namespace Cuemon.Runtime.Caching +{ + /// + /// An interface that is used to provide cache implementations for an application. + /// + /// The type of the key in the cache. + public interface ICacheEnumerable : IEnumerable> + { + /// + /// Gets or sets a value in the cache by using the default indexer property for an instance of the class. + /// + /// The unique identifier for the cache value to get or set. + /// The optional named group associated with the cache value. + /// The value in the cache for the specified , if the entry exists; otherwise, null. + object this[string key, string ns = CacheEntry.NoScope] + { + get; + set; + } + + /// + /// Gets the function delegate that is responsible for providing a unique identifier for the cache entry. + /// + /// The function delegate that is responsible for providing a unique identifier for the cache entry. + Func KeyProvider { get; } + + /// + /// Inserts a cache entry into the cache as a instance, and adds details about how the entry should be evicted. + /// + /// The object representing the cached value for a cache entry. + /// The object that contains expiration details for a specific cache entry. + bool Add(CacheEntry entry, CacheInvalidation invalidation); + + /// + /// Determines whether a cache entry exists in the cache. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// true if the cache contains a cache entry whose key matches ; otherwise, false. + bool Contains(string key, string ns = CacheEntry.NoScope); + + /// + /// Gets the number of entries associated with the contained in the cache. + /// + /// The optional namespace that provides a scope to the cache. + /// The number of entries contained in the cache. + int Count(string ns = CacheEntry.NoScope); + + /// + /// Removes all entries associated with the contained in the cache. + /// + /// The optional namespace that provides a scope to the cache. + void RemoveAll(string ns = CacheEntry.NoScope); + + /// + /// Returns an entry from the cache. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// A reference to the value in the cache container that is identified by , if the entry exists; otherwise, null. + object Get(string key, string ns = CacheEntry.NoScope); + + /// + /// Returns an entry from the cache as a instance. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// A reference to the that is identified by , if the entry exists; otherwise, null. + CacheEntry GetCacheEntry(string key, string ns = CacheEntry.NoScope); + + /// + /// Removes a cache entry from the cache. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// If the entry is found in the cache, a reference to the value in the cache container of the removed cache entry; otherwise, null. + object Remove(string key, string ns = default); + + /// + /// Inserts a cache entry into the cache. + /// + /// The unique identifier of the cache. + /// The stored value of the cache. + /// The object that contains expiration details for a specific cache entry. + /// The optional namespace that provides a scope to the cache. + void Set(string key, object value, CacheInvalidation invalidation, string ns = CacheEntry.NoScope); + + /// + /// Attempts to get the associated with the specified from the cache. + /// + /// The unique identifier for the cache entry. + /// When this method returns, contains the cache entry associated with the specified , or null if the operation failed. + /// true if the was found in the cache; otherwise, false. + bool TryGetCacheEntry(string key, out CacheEntry cacheEntry); + + /// + /// Attempts to get the associated with the specified and from the cache. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// When this method returns, contains the cache entry associated with the specified and , or null if the operation failed. + /// true if the was found in the cache; otherwise, false. + bool TryGetCacheEntry(string key, string ns, out CacheEntry cacheEntry); + + /// + /// Attempts to get the value associated with the specified from the cache. + /// + /// The unique identifier for the cache entry. + /// When this method returns, contains the value associated with the specified , or null if the operation failed. + /// true if the was found in the cache; otherwise, false. + bool TryGet(string key, out object value); + + /// + /// Attempts to get the value associated with the specified and from the cache. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// When this method returns, contains the value associated with the specified and , or null if the operation failed. + /// true if the was found in the cache; otherwise, false. + bool TryGet(string key, string ns, out object value); + } +} \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/SlimMemoryCache.cs b/src/Cuemon.Runtime.Caching/SlimMemoryCache.cs new file mode 100644 index 000000000..f244b64e1 --- /dev/null +++ b/src/Cuemon.Runtime.Caching/SlimMemoryCache.cs @@ -0,0 +1,394 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using Cuemon.Collections.Generic; +using Cuemon.Threading; + +namespace Cuemon.Runtime.Caching +{ + /// + /// Represents the type that implements an in-memory cache for an application. + /// + /// + /// + public class SlimMemoryCache : Disposable, ICacheEnumerable + { + private readonly ConcurrentDictionary _innerCaches = new ConcurrentDictionary(); + private readonly Timer _expirationTimer; + + /// + /// Initializes a new instance of the class. + /// + /// The which may be configured. + public SlimMemoryCache(Action setup = null) + { + var options = Patterns.Configure(setup); + KeyProvider = options.KeyProvider; + if (options.EnableCleanup) + { + _expirationTimer = TimerFactory.CreateNonCapturingTimer(state => ((SlimMemoryCache)state).OnAutomatedSweepCleanup(), this, options.FirstSweep, options.SucceedingSweep); + } + } + + /// + /// Gets or sets a value in the cache by using the default indexer property for an instance of the class. + /// + /// The unique identifier for the cache value to get or set. + /// The optional namespace that provides a scope to the cache. + /// The value in the cache for the specified , if the entry exists; otherwise, null. + public object this[string key, string ns = CacheEntry.NoScope] + { + get + { + if (TryGetCacheEntry(key, ns, out var cache)) + { + return cache.Value; + } + return null; + } + set + { + if (TryGetCacheEntry(key, ns, out var cache)) + { + cache.Value = value; + } + } + } + + /// + /// Gets the function delegate that is responsible for providing a unique identifier for the cache entry. + /// + /// The function delegate that is responsible for providing a unique identifier for the cache entry. + public Func KeyProvider { get; } + + /// + /// Inserts a cache entry into the cache and adds details about how the entry should be evicted. + /// + /// The unique identifier of the cache. + /// The stored value of the cache. + /// The absolute expiration date time value from when the cached becomes invalid and is removed from the cache. + /// The optional namespace that provides a scope to the cache. + /// true if insertion succeeded; otherwise, false when there is already an entry in the cache with the same key. + /// + /// cannot be null. + /// + public bool Add(string key, object value, DateTime absoluteExpiration, string ns = CacheEntry.NoScope) + { + return Add(new CacheEntry(key, value, ns), new CacheInvalidation(absoluteExpiration)); + } + + /// + /// Inserts a cache entry into the cache and adds details about how the entry should be evicted. + /// + /// The unique identifier of the cache. + /// The stored value of the cache. + /// An implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached becomes invalid and is removed from the cache. + /// The optional namespace that provides a scope to the cache. + /// true if insertion succeeded; otherwise, false when there is already an entry in the cache with the same key. + /// + /// cannot be null. + /// + public bool Add(string key, object value, IDependency dependency, string ns = CacheEntry.NoScope) + { + return Add(new CacheEntry(key, value, ns), new CacheInvalidation(Arguments.Yield(dependency))); + } + + /// + /// Inserts a cache entry into the cache and adds details about how the entry should be evicted. + /// + /// The unique identifier of the cache. + /// The stored value of the cache. + /// A sequence of implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached becomes invalid and is removed from the cache. + /// The optional namespace that provides a scope to the cache. + /// true if insertion succeeded; otherwise, false when there is already an entry in the cache with the same key. + /// + /// cannot be null. + /// + public bool Add(string key, object value, IEnumerable dependencies, string ns = CacheEntry.NoScope) + { + return Add(new CacheEntry(key, value, ns), new CacheInvalidation(dependencies)); + } + + /// + /// Inserts a cache entry into the cache and adds details about how the entry should be evicted. + /// + /// The unique identifier of the cache. + /// The stored value of the cache. + /// The sliding expiration time from when the cached becomes invalid and is removed from the cache. + /// The optional namespace that provides a scope to the cache. + /// true if insertion succeeded; otherwise, false when there is already an entry in the cache with the same key. + /// + /// cannot be null. + /// + public bool Add(string key, object value, TimeSpan slidingExpiration, string ns = CacheEntry.NoScope) + { + return Add(new CacheEntry(key, value, ns), new CacheInvalidation(slidingExpiration)); + } + + /// + /// Inserts a cache entry into the cache and adds details about how the entry should be evicted. + /// + /// The object representing the cached value for a cache entry. + /// The object that contains expiration details for a specific cache entry. + /// true if insertion succeeded; otherwise, false when there is already an entry in the cache with the same key. + /// + /// cannot be null -or- + /// cannot be null. + /// + public bool Add(CacheEntry entry, CacheInvalidation invalidation) + { + Validator.ThrowIfNull(entry, nameof(entry)); + Validator.ThrowIfNull(invalidation, nameof(invalidation)); + var nsKey = KeyProvider(entry.Key, entry.Namespace); + return _innerCaches.TryAdd(nsKey, entry.SetInvalidation(invalidation).StartDependencies()); + } + + /// + /// Determines whether a cache entry exists in the cache. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// true if the cache contains a cache entry whose key matches ; otherwise, false. + /// + /// cannot be null. + /// + public bool Contains(string key, string ns = CacheEntry.NoScope) + { + return TryGetCacheEntry(key, ns, out _); + } + + /// + /// Gets the number of entries associated with the contained in the cache. + /// + /// The optional namespace that provides a scope to the cache. + /// The number of entries contained in the cache. + public int Count(string ns = CacheEntry.NoScope) + { + return ListCacheEntries(ns).Count; + } + + /// + /// Removes all entries associated with the contained in the cache. + /// + /// The optional namespace that provides a scope to the cache. + public void RemoveAll(string ns = CacheEntry.NoScope) + { + var entries = ListCacheEntries(ns); + foreach (var cacheEntry in entries) + { + Remove(cacheEntry.Key, cacheEntry.Namespace); + } + } + + /// + /// Returns an entry from the cache. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// A reference to the value in the cache container that is identified by , if the entry exists; otherwise, null. + /// + /// cannot be null. + /// + public object Get(string key, string ns = CacheEntry.NoScope) + { + return GetCacheEntry(key, ns)?.Value; + } + + /// + /// Returns an entry from the cache as a instance. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// A reference to the that is identified by , if the entry exists; otherwise, null. + /// + /// cannot be null. + /// + public CacheEntry GetCacheEntry(string key, string ns = CacheEntry.NoScope) + { + if (TryGetCacheEntry(key, ns, out var cacheEntry)) + { + return cacheEntry; + } + return null; + } + + /// + /// Removes a cache entry from the cache. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// If the entry is found in the cache, a reference to the value in the cache container of the removed cache entry; otherwise, null. + /// + /// cannot be null. + /// + public object Remove(string key, string ns = CacheEntry.NoScope) + { + Validator.ThrowIfNull(key, nameof(key)); + var nsKey = KeyProvider(key, ns); + if (_innerCaches.TryRemove(nsKey, out var cacheEntry)) + { + return cacheEntry.Value; + } + return null; + } + + /// + /// Inserts a cache entry into the cache. + /// + /// The unique identifier of the cache. + /// The stored value of the cache. + /// The object that contains expiration details for a specific cache entry. + /// The optional namespace that provides a scope to the cache. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// The method always puts a cache value in the cache, regardless whether an entry already exists with the same key. If the specified entry does not exist in the cache, a new cache entry is inserted. If the specified entry exists, its value is updated. + public void Set(string key, object value, CacheInvalidation invalidation, string ns = CacheEntry.NoScope) + { + Validator.ThrowIfNull(key, nameof(key)); + if (TryGetCacheEntry(key, ns, out var cacheEntry)) + { + cacheEntry.Value = value; + cacheEntry.Refresh(); + } + else + { + Add(new CacheEntry(key, value, ns), invalidation); + } + } + + /// + /// Attempts to get the associated with the specified from the cache. + /// + /// The unique identifier for the cache entry. + /// When this method returns, contains the cache entry associated with the specified , or null if the operation failed. + /// true if the was found in the cache; otherwise, false. + public bool TryGetCacheEntry(string key, out CacheEntry cacheEntry) + { + return TryGetCacheEntry(key, CacheEntry.NoScope, out cacheEntry); + } + + /// + /// Attempts to get the associated with the specified and from the cache. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// When this method returns, contains the cache entry associated with the specified and , or null if the operation failed. + /// true if the was found in the cache; otherwise, false. + /// + /// cannot be null. + /// + public bool TryGetCacheEntry(string key, string ns, out CacheEntry cacheEntry) + { + Validator.ThrowIfNull(key, nameof(key)); + cacheEntry = null; + var utcNow = DateTime.UtcNow; + var nsKey = KeyProvider(key, ns); + if (_innerCaches.TryGetValue(nsKey, out var ce)) + { + var hasCacheExpired = ce.HasExpired(utcNow); + if (ce.CanExpire && hasCacheExpired) + { + Remove(key, ns); + return false; + } + + if (ce.CanExpire && !hasCacheExpired) + { + cacheEntry = ce; + ce.Refresh(); + } + return true; + } + return false; + } + + /// + /// Attempts to get the value associated with the specified from the cache. + /// + /// The unique identifier for the cache entry. + /// When this method returns, contains the value associated with the specified , or null if the operation failed. + /// true if the was found in the cache; otherwise, false. + public bool TryGet(string key, out object value) + { + return TryGet(key, CacheEntry.NoScope, out value); + } + + /// + /// Attempts to get the value associated with the specified and from the cache. + /// + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. + /// When this method returns, contains the value associated with the specified and , or null if the operation failed. + /// true if the was found in the cache; otherwise, false. + /// + /// cannot be null. + /// + public bool TryGet(string key, string ns, out object value) + { + var success = TryGetCacheEntry(key, ns, out var cacheEntry); + value = success ? cacheEntry.Value : null; + return success; + } + + private IList ListCacheEntries(string ns) + { + var utcNow = DateTime.UtcNow; + var entries = new List(); + var snapshot = new List(_innerCaches.Values); + foreach (var cacheEntry in snapshot) + { + if (cacheEntry == null) { continue; } // this can happen if a cache has been removed + if (cacheEntry.CanExpire && cacheEntry.HasExpired(utcNow)) { continue; } + if (cacheEntry.Namespace == ns) + { + entries.Add(cacheEntry); + } + } + return entries; + } + + /// + /// Called when this object is being disposed by either or having disposing set to true and is false. + /// + protected override void OnDisposeManagedResources() + { + _expirationTimer?.Dispose(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// An enumerator that can be used to iterate through the collection. + public IEnumerator> GetEnumerator() + { + return _innerCaches.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// An object that can be used to iterate through the collection. + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private void OnAutomatedSweepCleanup() + { + var utcNow = DateTime.UtcNow; + var snapshot = new List(_innerCaches.Values); + if (snapshot.Count > 0) + { + foreach (var cacheEntry in snapshot) + { + if (cacheEntry == null) { continue; } + if (cacheEntry.CanExpire && cacheEntry.HasExpired(utcNow)) { Remove(cacheEntry.Key, cacheEntry.Namespace); } + } + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/SlimMemoryCacheOptions.cs b/src/Cuemon.Runtime.Caching/SlimMemoryCacheOptions.cs new file mode 100644 index 000000000..c9f16fbc5 --- /dev/null +++ b/src/Cuemon.Runtime.Caching/SlimMemoryCacheOptions.cs @@ -0,0 +1,70 @@ +using System; + +namespace Cuemon.Runtime.Caching +{ + /// + /// Configuration options for . + /// + public class SlimMemoryCacheOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// true + /// + /// + /// + /// After 30 seconds + /// + /// + /// + /// Every 2 minutes + /// + /// + /// + /// (key, ns) => Generate.HashCode64(ns == Cache.NoScope ? key.ToUpperInvariant() : $"{key}^{nameof(SlimMemoryCache)}^{ns}".ToUpperInvariant()); + /// + /// + /// + public SlimMemoryCacheOptions() + { + EnableCleanup = true; + FirstSweep = TimeSpan.FromSeconds(30); + SucceedingSweep = TimeSpan.FromMinutes(2); + KeyProvider = (key, ns) => Generate.HashCode64(ns == CacheEntry.NoScope ? key.ToUpperInvariant() : $"{key}^{nameof(SlimMemoryCache)}^{ns}".ToUpperInvariant()); + } + + /// + /// Gets or sets a value indicating whether a periodic sweep clean-up is done on the cache. + /// + /// true if a periodic sweep clean-up is done on the cache; otherwise, false. + public bool EnableCleanup { get; set; } + + /// + /// Gets or sets the that specifies the amount of time to wait before the initial first sweep clean-up. + /// + /// The that specifies the amount of time to wait before the initial first sweep clean-up. + public TimeSpan FirstSweep { get; set; } + + /// + /// Gets or sets the that specifies the interval for every succeeding sweep clean-up after the initial . + /// + /// The that specifies the interval for every succeeding sweep clean-up. + public TimeSpan SucceedingSweep { get; set; } + + /// + /// Gets or sets the function delegate that is responsible for providing a unique identifier for a cache entry. + /// + /// The function delegate that is responsible for providing a unique identifier for a cache entry. + public Func KeyProvider { get; set; } + } +} \ No newline at end of file From 4dbfe01c6d570074dd3a6604a541b19b04777222 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 16:59:58 +0200 Subject: [PATCH 197/385] The complemental unit test to commit 683d3e44. --- .../Assets/CountdownDependency.cs | 42 +++ .../Cuemon.Runtime.Caching.Tests.csproj | 16 + .../SlimMemoryCacheTest.cs | 279 ++++++++++++++++++ 3 files changed, 337 insertions(+) create mode 100644 test/Cuemon.Runtime.Caching.Tests/Assets/CountdownDependency.cs create mode 100644 test/Cuemon.Runtime.Caching.Tests/Cuemon.Runtime.Caching.Tests.csproj create mode 100644 test/Cuemon.Runtime.Caching.Tests/SlimMemoryCacheTest.cs diff --git a/test/Cuemon.Runtime.Caching.Tests/Assets/CountdownDependency.cs b/test/Cuemon.Runtime.Caching.Tests/Assets/CountdownDependency.cs new file mode 100644 index 000000000..2fead554c --- /dev/null +++ b/test/Cuemon.Runtime.Caching.Tests/Assets/CountdownDependency.cs @@ -0,0 +1,42 @@ +using System; +using System.Diagnostics; +using System.Threading; +using Cuemon.Threading; + +namespace Cuemon.Runtime.Caching.Assets +{ + public class CountdownDependency : Dependency, IDisposable + { + private Timer _handler; + private TimeSpan _timer; + private Stopwatch _sw = Stopwatch.StartNew(); + + public CountdownDependency(TimeSpan timer) + { + _timer = timer; + } + + private void OnCountdown() + { + _timer -= TimeSpan.FromSeconds(1); + if (_timer < TimeSpan.Zero) + { + _timer = TimeSpan.Zero; + _handler?.Dispose(); + _handler = null; + } + } + + public override bool HasChanged => _timer == TimeSpan.Zero; + + public override void Start() + { + _handler = TimerFactory.CreateNonCapturingTimer(state => ((CountdownDependency)state).OnCountdown(), this, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); + } + + public void Dispose() + { + _handler?.Dispose(); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Runtime.Caching.Tests/Cuemon.Runtime.Caching.Tests.csproj b/test/Cuemon.Runtime.Caching.Tests/Cuemon.Runtime.Caching.Tests.csproj new file mode 100644 index 000000000..8948c9dd6 --- /dev/null +++ b/test/Cuemon.Runtime.Caching.Tests/Cuemon.Runtime.Caching.Tests.csproj @@ -0,0 +1,16 @@ + + + + Cuemon.Runtime.Caching + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Cuemon.Runtime.Caching.Tests/SlimMemoryCacheTest.cs b/test/Cuemon.Runtime.Caching.Tests/SlimMemoryCacheTest.cs new file mode 100644 index 000000000..b511d7cf1 --- /dev/null +++ b/test/Cuemon.Runtime.Caching.Tests/SlimMemoryCacheTest.cs @@ -0,0 +1,279 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit.Hosting; +using Cuemon.Runtime.Caching.Assets; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; +using Xunit.Priority; + +namespace Cuemon.Runtime.Caching +{ + [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] + public class SlimMemoryCacheTest : HostTest + { + private readonly SlimMemoryCache _cache; + private readonly SlimMemoryCacheOptions _cacheOptions = new SlimMemoryCacheOptions(); + + private const string Sliding30Namespace = "Sliding30"; + private const string Absolute30Namespace = "Absolute30"; + private const string Dependency30Namespace = "Dependency30"; + private const string Sliding60Namespace = "Sliding60"; + private const string Absolute60Namespace = "Absolute60"; + private const string Dependency60Namespace = "Dependency60"; + + private const int NumberOfItemsToCache = 5000; + + public SlimMemoryCacheTest(HostFixture hostFixture, ITestOutputHelper output = null) : base(hostFixture, output) + { + _cache = hostFixture.ServiceProvider.GetRequiredService(); + } + + [Fact] + public void Add_ShouldBeThreadSafeWhenAddingSameKeyInParallel() + { + var items = NumberOfItemsToCache; + var key = "cuemon"; + Parallel.For(0, items, i => + { + var nw = Guid.NewGuid(); + if (_cache.Add(key, nw, DateTime.MaxValue)) + { + Assert.Equal(nw, _cache[key]); + } + else + { + Assert.NotEqual(nw, _cache[key]); + } + }); + + Assert.Equal(1, _cache.Count()); + Assert.Equal(1, _cache.ToList().Count); + + _cache.Remove(key); + + Assert.Equal(0, _cache.Count()); + Assert.Equal(0, _cache.ToList().Count); + } + + [Fact] + public void Add_ShouldUpdateUsingPropertyIndexer() + { + var key = "cuemon"; + var expectedPriorToVersionSix = "Cuemon .NET Standard"; + var expectedFromVersionSix = "Cuemon for .NET"; + + _cache.Add(key, expectedPriorToVersionSix, DateTime.MaxValue); + + Assert.True(_cache.Contains(key)); + Assert.Equal(expectedPriorToVersionSix, _cache[key]); + + _cache[key] = expectedFromVersionSix; + + Assert.Equal(expectedFromVersionSix, _cache[key]); + + _cache.Remove(key); + + Assert.False(_cache.Contains(key)); + } + + [Fact, Priority(1)] + public void Clear_ShouldRemoveAllCacheEntriesBothLogicalAndActual() + { + var values = Enumerable.Range(0, 10000).ToList(); + + foreach (var value in values) + { + _cache.Add(Guid.NewGuid().ToString("N"), value, DateTime.MaxValue); + } + + Assert.Equal(values.Count, _cache.Count()); + Assert.Equal(values.Count, _cache.ToList().Count); + Assert.True(values.Count == _cache.Select(pair => pair.Value.CanExpire).Count(), "values.Count == _cache.Select(pair => pair.Value.CanExpire).Count()"); + + _cache.RemoveAll(); + + Assert.Equal(0, _cache.Count()); + Assert.Equal(0, _cache.ToList().Count); + } + + [Fact, Priority(2)] + public void Add_ShouldHandleLargeLoadWithoutCollisionUsingSlidingExpirationOfOneMinute() + { + var items = NumberOfItemsToCache; + var expires = TimeSpan.FromMinutes(1); + var keys = Generate.RangeOf(items, i => Guid.NewGuid().ToString("N")).ToList(); + var bag = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the SlimMemoryCache + Parallel.ForEach(keys, key => + { + bag.Add(_cacheOptions.KeyProvider(key, Sliding60Namespace)); + _cache.Add(key, Generate.RandomString(5), expires, Sliding60Namespace); + }); + + Assert.Equal(0, _cache.Count()); + Assert.Equal(items, _cache.Count(Sliding60Namespace)); + Assert.Equal(items, _cache.ToList().Count); + Assert.True(bag.OrderBy(l => l).SequenceEqual(_cache.Where(pair => pair.Value.Namespace == Sliding60Namespace).Select(pair => pair.Key).OrderBy(l => l))); // insure thread safety validation + } + + [Fact, Priority(3)] + public void Add_ShouldHandleLargeLoadWithoutCollisionUsingSlidingExpirationOfThirtySecondsWithNamespaceSpecification() + { + var items = NumberOfItemsToCache; + var expires = TimeSpan.FromSeconds(30); + var keys = Generate.RangeOf(items, i => Guid.NewGuid().ToString("N")).ToList(); + var bag = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the SlimMemoryCache + Parallel.ForEach(keys, key => + { + bag.Add(_cacheOptions.KeyProvider(key, Sliding30Namespace)); + _cache.Add(key, Generate.RandomString(5), expires, Sliding30Namespace); + }); + + Assert.Equal(0, _cache.Count()); + Assert.Equal(keys.Count, _cache.Count(Sliding30Namespace)); + Assert.Equal(items * 2, _cache.ToList().Count); + Assert.True(bag.OrderBy(l => l).SequenceEqual(_cache.Where(pair => pair.Value.Namespace == Sliding30Namespace).Select(pair => pair.Key).OrderBy(l => l))); // insure thread safety validation + } + + [Fact, Priority(4)] + public void Add_ShouldHandleLargeLoadWithoutCollisionUsingAbsoluteExpirationOfOneMinute() + { + var items = NumberOfItemsToCache; + var expires = DateTime.UtcNow.AddMinutes(1); + var keys = Generate.RangeOf(items, i => Guid.NewGuid().ToString("N")).ToList(); + var bag = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the SlimMemoryCache + Parallel.ForEach(keys, key => + { + bag.Add(_cacheOptions.KeyProvider(key, Absolute60Namespace)); + _cache.Add(key, Generate.RandomString(5), expires, Absolute60Namespace); + }); + + Assert.Equal(0, _cache.Count()); + Assert.Equal(items, _cache.Count(Absolute60Namespace)); + Assert.Equal(items * 3, _cache.ToList().Count); + Assert.True(bag.OrderBy(l => l).SequenceEqual(_cache.Where(pair => pair.Value.Namespace == Absolute60Namespace).Select(pair => pair.Key).OrderBy(l => l))); // insure thread safety validation + } + + [Fact, Priority(5)] + public void Add_ShouldHandleLargeLoadWithoutCollisionUsingAbsoluteExpirationOfThirtySecondsWithNamespaceSpecification() + { + var items = NumberOfItemsToCache; + var expires = DateTime.UtcNow.AddSeconds(30); + var keys = Generate.RangeOf(items, i => Guid.NewGuid().ToString("N")).ToList(); + var bag = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the SlimMemoryCache + Parallel.ForEach(keys, key => + { + bag.Add(_cacheOptions.KeyProvider(key, Absolute30Namespace)); + _cache.Add(key, Generate.RandomString(5), expires, Absolute30Namespace); + }); + + Assert.Equal(0, _cache.Count()); + Assert.Equal(keys.Count, _cache.Count(Absolute30Namespace)); + Assert.Equal(items * 4, _cache.ToList().Count); + Assert.True(bag.OrderBy(l => l).SequenceEqual(_cache.Where(pair => pair.Value.Namespace == Absolute30Namespace).Select(pair => pair.Key).OrderBy(l => l))); // insure thread safety validation + } + + [Fact, Priority(6)] + public void Add_ShouldHandleLargeLoadWithoutCollisionUsingDependencyExpirationOfOneMinute() + { + var items = NumberOfItemsToCache; + var expires = new Func(() => new CountdownDependency(TimeSpan.FromMinutes(1))); + var keys = Generate.RangeOf(items, i => Guid.NewGuid().ToString("N")).ToList(); + var bag = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the SlimMemoryCache + Parallel.ForEach(keys, key => + { + bag.Add(_cacheOptions.KeyProvider(key, Dependency60Namespace)); + _cache.Add(key, Generate.RandomString(5), expires(), Dependency60Namespace); + }); + + Assert.Equal(0, _cache.Count()); + Assert.Equal(items, _cache.Count(Dependency60Namespace)); + Assert.Equal(items * 5, _cache.ToList().Count); + Assert.True(bag.OrderBy(l => l).SequenceEqual(_cache.Where(pair => pair.Value.Namespace == Dependency60Namespace).Select(pair => pair.Key).OrderBy(l => l))); // insure thread safety validation + } + + [Fact, Priority(7)] + public void Add_ShouldHandleLargeLoadWithoutCollisionUsingDependencyExpirationOfThirtySecondsWithNamespaceSpecification() + { + var items = NumberOfItemsToCache; + var expires = new Func(() => new CountdownDependency(TimeSpan.FromSeconds(30))); + var keys = Generate.RangeOf(items, i => Guid.NewGuid().ToString("N")).ToList(); + var bag = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the SlimMemoryCache + Parallel.ForEach(keys, key => + { + bag.Add(_cacheOptions.KeyProvider(key, Dependency30Namespace)); + _cache.Add(key, Generate.RandomString(5), expires(), Dependency30Namespace); + }); + + Assert.Equal(0, _cache.Count()); + Assert.Equal(items, _cache.Count(Dependency30Namespace)); + Assert.Equal(items * 6, _cache.ToList().Count); + Assert.True(bag.OrderBy(l => l).SequenceEqual(_cache.Where(pair => pair.Value.Namespace == Dependency30Namespace).Select(pair => pair.Key).OrderBy(l => l))); // insure thread safety validation + } + + [Fact, Priority(8)] + public void Add_VerifyBothLogicalAndActualCacheRemovalUponExpirationForThirtySecondsNamespaceSpecification() + { + Thread.Sleep(TimeSpan.FromSeconds(30)); + + Assert.Equal(0, _cache.Count(Dependency30Namespace)); + Assert.Equal(0, _cache.Count(Sliding30Namespace)); + Assert.Equal(0, _cache.Count(Absolute30Namespace)); + + Assert.Equal(NumberOfItemsToCache, _cache.Where(pair => pair.Value.Namespace == Dependency30Namespace).ToList().Count); + Assert.Equal(NumberOfItemsToCache, _cache.Where(pair => pair.Value.Namespace == Sliding30Namespace).ToList().Count); + Assert.Equal(NumberOfItemsToCache, _cache.Where(pair => pair.Value.Namespace == Absolute30Namespace).ToList().Count); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Where(pair => pair.Value.Namespace == Dependency30Namespace).ToList().Count); + Assert.Equal(0, _cache.Where(pair => pair.Value.Namespace == Sliding30Namespace).ToList().Count); + Assert.Equal(0, _cache.Where(pair => pair.Value.Namespace == Absolute30Namespace).ToList().Count); + } + + [Fact, Priority(9)] + public void Add_VerifyBothLogicalAndActualCacheRemovalUponExpirationForSixtySecondsNamespaceSpecification() + { + Thread.Sleep(TimeSpan.FromSeconds(20)); + + Assert.Equal(0, _cache.Count(Dependency60Namespace)); + Assert.Equal(0, _cache.Count(Sliding60Namespace)); + Assert.Equal(0, _cache.Count(Absolute60Namespace)); + + Assert.Equal(NumberOfItemsToCache, _cache.Where(pair => pair.Value.Namespace == Dependency60Namespace).ToList().Count); + Assert.Equal(NumberOfItemsToCache, _cache.Where(pair => pair.Value.Namespace == Sliding60Namespace).ToList().Count); + Assert.Equal(NumberOfItemsToCache, _cache.Where(pair => pair.Value.Namespace == Absolute60Namespace).ToList().Count); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Where(pair => pair.Value.Namespace == Dependency60Namespace).ToList().Count); + Assert.Equal(0, _cache.Where(pair => pair.Value.Namespace == Sliding60Namespace).ToList().Count); + Assert.Equal(0, _cache.Where(pair => pair.Value.Namespace == Absolute60Namespace).ToList().Count); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddSingleton>(o => + { + o.FirstSweep = TimeSpan.FromSeconds(35); + o.SucceedingSweep = TimeSpan.FromSeconds(5); + }); + services.AddSingleton(); + } + } +} \ No newline at end of file From 95d13caf015d14e1be30bf621c98fbb2b6a4f3fe Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 17:01:22 +0200 Subject: [PATCH 198/385] Changed product name to Cuemon for .NET. Also, updated gitignore. --- .gitignore | 1 + Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 990d5bc38..1ad672d44 100644 --- a/.gitignore +++ b/.gitignore @@ -224,3 +224,4 @@ ModelManifest.xml /docfx/wwwroot /docfx/api/**/*.yml /docfx/**/*.manifest +/.vscode/docfx-assistant diff --git a/Directory.Build.props b/Directory.Build.props index a0cba37e4..71be3e5a5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -17,7 +17,7 @@ Copyright © Geekle 2009-2020. All rights reserved. Michael Mortensen Geekle - Cuemon + Cuemon for .NET https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png https://www.cuemon.net/ MIT From ab612cb0c51d3a357f4c1240c125bb314a433c7f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 17:02:51 +0200 Subject: [PATCH 199/385] In regards to commit 683d3e44 follows here a comprehensive set of extension methods for making the cache even smoother to work with. Includes Memoization and complemental unit tests. --- Cuemon.sln | 49 +- .../Cuemon.Extensions.Reflection.csproj | 1 - .../CacheEnumerableExtensions.cs | 743 ++++++++++++++ .../Cuemon.Extensions.Runtime.Caching.csproj | 19 + .../Properties/AssemblyInfo.cs | 4 + .../Assets/CountdownDependency.cs | 43 + .../CacheEnumerableExtensionsTest.cs | 949 ++++++++++++++++++ ...on.Extensions.Runtime.Caching.Tests.csproj | 12 + 8 files changed, 1805 insertions(+), 15 deletions(-) create mode 100644 src/Cuemon.Extensions.Runtime.Caching/CacheEnumerableExtensions.cs create mode 100644 src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj create mode 100644 src/Cuemon.Extensions.Runtime.Caching/Properties/AssemblyInfo.cs create mode 100644 test/Cuemon.Extensions.Runtime.Caching.Tests/Assets/CountdownDependency.cs create mode 100644 test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs create mode 100644 test/Cuemon.Extensions.Runtime.Caching.Tests/Cuemon.Extensions.Runtime.Caching.Tests.csproj diff --git a/Cuemon.sln b/Cuemon.sln index 5ccfed1d8..0a3748e79 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -109,13 +109,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Security.Cryptograph EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Security.Cryptography.Tests", "test\Cuemon.Security.Cryptography.Tests\Cuemon.Security.Cryptography.Tests.csproj", "{5D67081C-4458-41AA-A1F5-FAC974D29FDF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Xunit.Hosting", "src\Cuemon.Extensions.Xunit.Hosting\Cuemon.Extensions.Xunit.Hosting.csproj", "{D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Hosting", "src\Cuemon.Extensions.Xunit.Hosting\Cuemon.Extensions.Xunit.Hosting.csproj", "{1E0BDF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Hosting", "src\Cuemon.Extensions.Hosting\Cuemon.Extensions.Hosting.csproj", "{87BB6200-C51C-4085-8CE3-83B3C7A4FD8F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Hosting", "src\Cuemon.Extensions.Hosting\Cuemon.Extensions.Hosting.csproj", "{1D0BDF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Xunit.Tests", "test\Cuemon.Extensions.Xunit.Tests\Cuemon.Extensions.Xunit.Tests.csproj", "{2108E7E7-F002-481C-B17F-918E76D98378}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Tests", "test\Cuemon.Extensions.Xunit.Tests\Cuemon.Extensions.Xunit.Tests.csproj", "{2108E7E7-F002-481C-B17F-918E76D98378}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Hosting.Xunit.Tests", "test\Cuemon.Extensions.Hosting.Xunit.Tests\Cuemon.Extensions.Hosting.Xunit.Tests.csproj", "{86B43822-0733-416E-8DA2-666C5657974F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Hosting.Xunit.Tests", "test\Cuemon.Extensions.Hosting.Xunit.Tests\Cuemon.Extensions.Hosting.Xunit.Tests.csproj", "{86B43822-0733-416E-8DA2-666C5657974F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Runtime.Caching.Tests", "test\Cuemon.Runtime.Caching.Tests\Cuemon.Runtime.Caching.Tests.csproj", "{581174AB-62AA-4A04-85DE-4F9E307C9712}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Runtime.Caching", "src\Cuemon.Extensions.Runtime.Caching\Cuemon.Extensions.Runtime.Caching.csproj", "{487E6256-B4CA-4E3D-935D-F775B98DCDF2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Runtime.Caching.Tests", "test\Cuemon.Extensions.Runtime.Caching.Tests\Cuemon.Extensions.Runtime.Caching.Tests.csproj", "{0F614FD1-BC7C-4F7F-9847-D3675614576C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -327,14 +333,14 @@ Global {5D67081C-4458-41AA-A1F5-FAC974D29FDF}.Debug|Any CPU.Build.0 = Debug|Any CPU {5D67081C-4458-41AA-A1F5-FAC974D29FDF}.Release|Any CPU.ActiveCfg = Release|Any CPU {5D67081C-4458-41AA-A1F5-FAC974D29FDF}.Release|Any CPU.Build.0 = Release|Any CPU - {D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9}.Release|Any CPU.Build.0 = Release|Any CPU - {87BB6200-C51C-4085-8CE3-83B3C7A4FD8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {87BB6200-C51C-4085-8CE3-83B3C7A4FD8F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {87BB6200-C51C-4085-8CE3-83B3C7A4FD8F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {87BB6200-C51C-4085-8CE3-83B3C7A4FD8F}.Release|Any CPU.Build.0 = Release|Any CPU + {1E0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.Build.0 = Release|Any CPU + {1D0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.Build.0 = Release|Any CPU {2108E7E7-F002-481C-B17F-918E76D98378}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2108E7E7-F002-481C-B17F-918E76D98378}.Debug|Any CPU.Build.0 = Debug|Any CPU {2108E7E7-F002-481C-B17F-918E76D98378}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -343,6 +349,18 @@ Global {86B43822-0733-416E-8DA2-666C5657974F}.Debug|Any CPU.Build.0 = Debug|Any CPU {86B43822-0733-416E-8DA2-666C5657974F}.Release|Any CPU.ActiveCfg = Release|Any CPU {86B43822-0733-416E-8DA2-666C5657974F}.Release|Any CPU.Build.0 = Release|Any CPU + {581174AB-62AA-4A04-85DE-4F9E307C9712}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {581174AB-62AA-4A04-85DE-4F9E307C9712}.Debug|Any CPU.Build.0 = Debug|Any CPU + {581174AB-62AA-4A04-85DE-4F9E307C9712}.Release|Any CPU.ActiveCfg = Release|Any CPU + {581174AB-62AA-4A04-85DE-4F9E307C9712}.Release|Any CPU.Build.0 = Release|Any CPU + {487E6256-B4CA-4E3D-935D-F775B98DCDF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {487E6256-B4CA-4E3D-935D-F775B98DCDF2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {487E6256-B4CA-4E3D-935D-F775B98DCDF2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {487E6256-B4CA-4E3D-935D-F775B98DCDF2}.Release|Any CPU.Build.0 = Release|Any CPU + {0F614FD1-BC7C-4F7F-9847-D3675614576C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0F614FD1-BC7C-4F7F-9847-D3675614576C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0F614FD1-BC7C-4F7F-9847-D3675614576C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0F614FD1-BC7C-4F7F-9847-D3675614576C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -399,10 +417,13 @@ Global {06559CB0-899C-4B48-AFB8-633CBF97A766} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {1B0BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} {5D67081C-4458-41AA-A1F5-FAC974D29FDF} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} - {D3B6CBCC-4E10-4C71-8CFF-30591B5FFFF9} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} - {87BB6200-C51C-4085-8CE3-83B3C7A4FD8F} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} + {1E0BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} + {1D0BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} {2108E7E7-F002-481C-B17F-918E76D98378} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {86B43822-0733-416E-8DA2-666C5657974F} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} + {581174AB-62AA-4A04-85DE-4F9E307C9712} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} + {487E6256-B4CA-4E3D-935D-F775B98DCDF2} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} + {0F614FD1-BC7C-4F7F-9847-D3675614576C} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2A848386-B682-4F6D-8254-B5F6247C3054} diff --git a/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj b/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj index 373eb6ae3..5200940eb 100644 --- a/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj +++ b/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj @@ -6,7 +6,6 @@ - Cuemon .NET Standard Cuemon.Extensions.Reflection Cuemon.Extensions.Reflection The Cuemon.Extensions.Reflection namespace contains extension methods and features related to the System.Reflection namespace. diff --git a/src/Cuemon.Extensions.Runtime.Caching/CacheEnumerableExtensions.cs b/src/Cuemon.Extensions.Runtime.Caching/CacheEnumerableExtensions.cs new file mode 100644 index 000000000..d42fa34bf --- /dev/null +++ b/src/Cuemon.Extensions.Runtime.Caching/CacheEnumerableExtensions.cs @@ -0,0 +1,743 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using Cuemon.Collections.Generic; +using Cuemon.Reflection; +using Cuemon.Runtime; +using Cuemon.Runtime.Caching; + +namespace Cuemon.Extensions.Runtime.Caching +{ + /// + /// Extension methods for the interface. + /// + public static class CacheEnumerableExtensions + { + /// + /// Represents a cache with a scope of Memoization. + /// + public const string MemoizationScope = "Memoization"; + + private const long MemoizationNullHashCode = 854726591; + + /// + /// Gets the value for the specified from the cache, or if the does not exists, adds a value to the cache using the specified function delegate . + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The unique identifier for the cache entry. + /// An implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a value for the . + /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value returned by if the was not in the cache. + public static TResult GetOrAdd(this ICacheEnumerable cache, string key, IDependency dependency, Func valueFactory) + { + return GetOrAdd(cache, key, CacheEntry.NoScope, dependency, valueFactory); + } + + /// + /// Gets the value for the specified from the cache, or if the does not exists, adds a value to the cache using the specified function delegate . + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. Default is . + /// An implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a value for the . + /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value returned by if the was not in the cache. + public static TResult GetOrAdd(this ICacheEnumerable cache, string key, string ns, IDependency dependency, Func valueFactory) + { + return GetOrAdd(cache, key, ns, Arguments.Yield(dependency), valueFactory); + } + + /// + /// Gets the value for the specified from the cache, or if the does not exists, adds a value to the cache using the specified function delegate . + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The unique identifier for the cache entry. + /// A sequence of implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a value for the . + /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value returned by if the was not in the cache. + public static TResult GetOrAdd(this ICacheEnumerable cache, string key, IEnumerable dependencies, Func valueFactory) + { + return GetOrAdd(cache, key, CacheEntry.NoScope, dependencies, valueFactory); + } + + /// + /// Gets the value for the specified from the cache, or if the does not exists, adds a value to the cache using the specified function delegate . + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. Default is . + /// A sequence of implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a value for the . + /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value returned by if the was not in the cache. + public static TResult GetOrAdd(this ICacheEnumerable cache, string key, string ns, IEnumerable dependencies, Func valueFactory) + { + return GetOrAdd(cache, key, ns, new CacheInvalidation(dependencies), valueFactory); + } + + /// + /// Gets the value for the specified from the cache, or if the does not exists, adds a value to the cache using the specified function delegate . + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The unique identifier for the cache entry. + /// The sliding expiration time from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a value for the . + /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value returned by if the was not in the cache. + public static TResult GetOrAdd(this ICacheEnumerable cache, string key, TimeSpan slidingExpiration, Func valueFactory) + { + return GetOrAdd(cache, key, CacheEntry.NoScope, slidingExpiration, valueFactory); + } + + /// + /// Gets the value for the specified from the cache, or if the does not exists, adds a value to the cache using the specified function delegate . + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. Default is . + /// The sliding expiration time from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a value for the . + /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value returned by if the was not in the cache. + public static TResult GetOrAdd(this ICacheEnumerable cache, string key, string ns, TimeSpan slidingExpiration, Func valueFactory) + { + return GetOrAdd(cache, key, ns, new CacheInvalidation(slidingExpiration), valueFactory); + } + + /// + /// Gets the value for the specified from the cache, or if the does not exists, adds a value to the cache using the specified function delegate . + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The unique identifier for the cache entry. + /// The absolute expiration date time value from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a value for the . + /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value returned by if the was not in the cache. + public static TResult GetOrAdd(this ICacheEnumerable cache, string key, DateTime absoluteExpiration, Func valueFactory) + { + return GetOrAdd(cache, key, CacheEntry.NoScope, absoluteExpiration, valueFactory); + } + + /// + /// Gets the value for the specified from the cache, or if the does not exists, adds a value to the cache using the specified function delegate . + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. Default is . + /// The absolute expiration date time value from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a value for the . + /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value returned by if the was not in the cache. + public static TResult GetOrAdd(this ICacheEnumerable cache, string key, string ns, DateTime absoluteExpiration, Func valueFactory) + { + return GetOrAdd(cache, key, ns, new CacheInvalidation(absoluteExpiration), valueFactory); + } + + /// + /// Gets the value for the specified from the cache, or if the does not exists, adds a value to the cache using the specified function delegate . + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The unique identifier for the cache entry. + /// The object that contains expiration details for a specific cache entry. + /// The function delegate used to provide a value for the . + /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value returned by if the was not in the cache. + public static TResult GetOrAdd(this ICacheEnumerable cache, string key, CacheInvalidation invalidation, Func valueFactory) + { + return GetOrAdd(cache, key, CacheEntry.NoScope, invalidation, valueFactory); + } + + /// + /// Gets the value for the specified from the cache, or if the does not exists, adds a value to the cache using the specified function delegate . + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The unique identifier for the cache entry. + /// The optional namespace that provides a scope to the cache. Default is . + /// The object that contains expiration details for a specific cache entry. + /// The function delegate used to provide a value for the . + /// The value for the specified . This will either be the existing value if the is already in the cache, or the new value returned by if the was not in the cache. + public static TResult GetOrAdd(this ICacheEnumerable cache, string key, string ns, CacheInvalidation invalidation, Func valueFactory) + { + Validator.ThrowIfNull(cache, nameof(cache)); + Validator.ThrowIfNull(key, nameof(key)); + Validator.ThrowIfNull(invalidation, nameof(invalidation)); + Validator.ThrowIfNull(valueFactory, nameof(valueFactory)); + + if (!cache.TryGet(key, ns, out var value)) + { + value = valueFactory(); + cache.Add(new CacheEntry(key, value, ns), invalidation); + } + return (TResult)value; + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// An implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IDependency dependency, Func valueFactory) + { + return Memoize(cache, Arguments.Yield(dependency), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// A sequence of implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IEnumerable dependencies, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(dependencies), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The sliding expiration time from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, TimeSpan slidingExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(slidingExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The absolute expiration date time value from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, DateTime absoluteExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(absoluteExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the value in the cache. + /// The to extend. + /// The object that contains expiration details for a specific cache entry. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, CacheInvalidation invalidation, Func valueFactory) + { + return delegate + { + var key = ComputeMemoizationCacheKey(valueFactory); + return Memoize(cache, key, invalidation, FuncFactory.Create(valueFactory)); + }; + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// An implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IDependency dependency, Func valueFactory) + { + return Memoize(cache, Arguments.Yield(dependency), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// A sequence of implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IEnumerable dependencies, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(dependencies), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The sliding expiration time from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, TimeSpan slidingExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(slidingExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The absolute expiration date time value from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, DateTime absoluteExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(absoluteExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The object that contains expiration details for a specific cache entry. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, CacheInvalidation invalidation, Func valueFactory) + { + return delegate (T arg) + { + var key = ComputeMemoizationCacheKey(valueFactory, arg); + return Memoize(cache, key, invalidation, FuncFactory.Create(valueFactory, arg)); + }; + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// An implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IDependency dependency, Func valueFactory) + { + return Memoize(cache, Arguments.Yield(dependency), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// A sequence of implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IEnumerable dependencies, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(dependencies), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The sliding expiration time from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, TimeSpan slidingExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(slidingExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The absolute expiration date time value from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, DateTime absoluteExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(absoluteExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The object that contains expiration details for a specific cache entry. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, CacheInvalidation invalidation, Func valueFactory) + { + return delegate (T1 arg1, T2 arg2) + { + var key = ComputeMemoizationCacheKey(valueFactory, arg1, arg2); + return Memoize(cache, key, invalidation, FuncFactory.Create(valueFactory, arg1, arg2)); + }; + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// An implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IDependency dependency, Func valueFactory) + { + return Memoize(cache, Arguments.Yield(dependency), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// A sequence of implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IEnumerable dependencies, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(dependencies), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The sliding expiration time from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, TimeSpan slidingExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(slidingExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The absolute expiration date time value from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, DateTime absoluteExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(absoluteExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The object that contains expiration details for a specific cache entry. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, CacheInvalidation invalidation, Func valueFactory) + { + return delegate (T1 arg1, T2 arg2, T3 arg3) + { + var key = ComputeMemoizationCacheKey(valueFactory, arg1, arg2, arg3); + return Memoize(cache, key, invalidation, FuncFactory.Create(valueFactory, arg1, arg2, arg3)); + }; + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// An implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IDependency dependency, Func valueFactory) + { + return Memoize(cache, Arguments.Yield(dependency), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// A sequence of implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IEnumerable dependencies, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(dependencies), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The sliding expiration time from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, TimeSpan slidingExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(slidingExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The absolute expiration date time value from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, DateTime absoluteExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(absoluteExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The object that contains expiration details for a specific cache entry. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, CacheInvalidation invalidation, Func valueFactory) + { + return delegate (T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + var key = ComputeMemoizationCacheKey(valueFactory, arg1, arg2, arg3, arg4); + return Memoize(cache, key, invalidation, FuncFactory.Create(valueFactory, arg1, arg2, arg3, arg4)); + }; + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// An implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IDependency dependency, Func valueFactory) + { + return Memoize(cache, Arguments.Yield(dependency), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// A sequence of implementations that monitors changes in the state of the data which a cache entry depends on. If a state change is registered, the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, IEnumerable dependencies, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(dependencies), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The sliding expiration time from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, TimeSpan slidingExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(slidingExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The absolute expiration date time value from when the cached entry becomes invalid and is removed from the cache. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, DateTime absoluteExpiration, Func valueFactory) + { + return Memoize(cache, new CacheInvalidation(absoluteExpiration), valueFactory); + } + + /// + /// Memoizes the specified in the cache for fast access. + /// + /// The type of the key in the cache. + /// The type of the first parameter of the delegate . + /// The type of the second parameter of the delegate . + /// The type of the third parameter of the delegate . + /// The type of the fourth parameter of the delegate . + /// The type of the fifth parameter of the delegate . + /// The type of the value in the cache. + /// The to extend. + /// The object that contains expiration details for a specific cache entry. + /// The function delegate used to provide a memoized value. + /// A memoized function delegate that is otherwise equivalent to . + public static Func Memoize(this ICacheEnumerable cache, CacheInvalidation invalidation, Func valueFactory) + { + return delegate (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) + { + var key = ComputeMemoizationCacheKey(valueFactory, arg1, arg2, arg3, arg4, arg5); + return Memoize(cache, key, invalidation, FuncFactory.Create(valueFactory, arg1, arg2, arg3, arg4, arg5)); + }; + } + + private static readonly object PadLock = new object(); + + private static TResult Memoize(ICacheEnumerable cache, string key, CacheInvalidation invalidation, FuncFactory valueFactory) where TTuple : Template + { + if (cache.TryGetCacheEntry(key, MemoizationScope, out var cacheEntry)) { return (TResult)cacheEntry.Value; } + lock (PadLock) + { + if (!cache.TryGet(key, MemoizationScope, out var value)) + { + value = valueFactory.ExecuteMethod(); + cache.Add(new CacheEntry(key, value, MemoizationScope), invalidation); + } + return (TResult)value; + } + } + + private static string ComputeMemoizationCacheKey(Delegate del, params object[] args) + { + var result = del == null || del.GetMethodInfo() == null + ? MemoizationNullHashCode.GetHashCode() + : MethodDescriptor.Create(del.GetMethodInfo()).ToString().GetHashCode(); + + foreach (var arg in args) + { + var current = arg ?? MemoizationNullHashCode; + var bytes = current as byte[]; + result ^= bytes == null + ? current.GetHashCode() + : Generate.HashCode32(bytes.Cast()); + } + + return result.ToString(CultureInfo.InvariantCulture); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj b/src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj new file mode 100644 index 000000000..274afe443 --- /dev/null +++ b/src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + 1f0bdf91-e7c7-4cb4-a39d-e1a5374c5602 + + + + Cuemon.Extensions.Runtime.Caching + Cuemon.Extensions.Runtime.Caching + The Cuemon.Extensions.Runtime.Caching namespace contains extension methods and features that greatly complements the Cuemon.Runtime.Caching namespace. + extension-methods extensions memoization get-or-add + + + + + + + \ No newline at end of file diff --git a/src/Cuemon.Extensions.Runtime.Caching/Properties/AssemblyInfo.cs b/src/Cuemon.Extensions.Runtime.Caching/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d1b06c7c3 --- /dev/null +++ b/src/Cuemon.Extensions.Runtime.Caching/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("7db84665-da22-440e-b60b-9ab4a48c4122")] \ No newline at end of file diff --git a/test/Cuemon.Extensions.Runtime.Caching.Tests/Assets/CountdownDependency.cs b/test/Cuemon.Extensions.Runtime.Caching.Tests/Assets/CountdownDependency.cs new file mode 100644 index 000000000..f55c5f198 --- /dev/null +++ b/test/Cuemon.Extensions.Runtime.Caching.Tests/Assets/CountdownDependency.cs @@ -0,0 +1,43 @@ +using System; +using System.Diagnostics; +using System.Threading; +using Cuemon.Runtime; +using Cuemon.Threading; + +namespace Cuemon.Extensions.Runtime.Caching.Assets +{ + public class CountdownDependency : Dependency, IDisposable + { + private Timer _handler; + private TimeSpan _timer; + private Stopwatch _sw = Stopwatch.StartNew(); + + public CountdownDependency(TimeSpan timer) + { + _timer = timer; + } + + private void OnCountdown() + { + _timer -= TimeSpan.FromSeconds(1); + if (_timer < TimeSpan.Zero) + { + _timer = TimeSpan.Zero; + _handler?.Dispose(); + _handler = null; + } + } + + public override bool HasChanged => _timer == TimeSpan.Zero; + + public override void Start() + { + _handler = TimerFactory.CreateNonCapturingTimer(state => ((CountdownDependency)state).OnCountdown(), this, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); + } + + public void Dispose() + { + _handler?.Dispose(); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs b/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs new file mode 100644 index 000000000..acd7a9c77 --- /dev/null +++ b/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs @@ -0,0 +1,949 @@ +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.Extensions.Runtime.Caching.Assets; +using Cuemon.Extensions.Xunit.Hosting; +using Cuemon.Runtime.Caching; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Extensions.Runtime.Caching +{ + public class CacheEnumerableExtensionsTest : HostTest + { + private readonly SlimMemoryCache _cache; + private readonly SlimMemoryCacheOptions _cacheOptions = new SlimMemoryCacheOptions(); + + public CacheEnumerableExtensionsTest(HostFixture hostFixture, ITestOutputHelper output = null) : base(hostFixture, output) + { + _cache = hostFixture.ServiceProvider.GetRequiredService(); + } + + [Fact] + public void GetOrAdd_ShouldCacheAndReturnItemInOneGoUsingSlidingExpirationOfTenSeconds() + { + var items = 25000; + var expires = TimeSpan.FromSeconds(10); + var keys = Generate.RangeOf(items, i => Guid.NewGuid().ToString("N")).ToList(); + var bag = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.ForEach(keys, key => + { + bag.Add(_cacheOptions.KeyProvider(key, CacheEntry.NoScope)); + var value = Generate.RandomString(5); + Assert.Equal(value, _cache.GetOrAdd(key, expires, () => value)); + }); + + Assert.Equal(items, _cache.Count()); + Assert.True(bag.OrderBy(l => l).SequenceEqual(_cache.Where(pair => pair.Value.Namespace == CacheEntry.NoScope).Select(pair => pair.Key).OrderBy(l => l))); // insure thread safety validation + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count()); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingSlidingExpirationOfTenSeconds() + { + var expires = TimeSpan.FromSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func(ExpensiveRandomString); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs()); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(17, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingSlidingExpirationOfTenSeconds() + { + var expires = TimeSpan.FromSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func(p1 => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1); + }); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs(3)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(3, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingSlidingExpirationOfTenSeconds() + { + var expires = TimeSpan.FromSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2); + }); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(2, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsingSlidingExpirationOfTenSeconds() + { + var expires = TimeSpan.FromSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2, p3) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2 + p3); + }); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1, 3)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(5, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsingSlidingExpirationOfTenSeconds() + { + var expires = TimeSpan.FromSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2, p3, p4) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2 + p3 + p4); + }); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1, 3, 2)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(7, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsingSlidingExpirationOfTenSeconds() + { + var expires = TimeSpan.FromSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2, p3, p4, p5) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2 + p3 + p4 + p5); + }); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1, 3, 2, 4)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(11, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingAbsoluteExpirationOfTenSeconds() + { + var expires = DateTime.UtcNow.AddSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func(ExpensiveRandomString); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs()); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(17, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingAbsoluteExpirationOfTenSeconds() + { + var expires = DateTime.UtcNow.AddSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func(p1 => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1); + }); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs(3)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(3, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingAbsoluteExpirationOfTenSeconds() + { + var expires = DateTime.UtcNow.AddSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2); + }); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(2, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsingAbsoluteExpirationOfTenSeconds() + { + var expires = DateTime.UtcNow.AddSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2, p3) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2 + p3); + }); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1, 3)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(5, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsingAbsoluteExpirationOfTenSeconds() + { + var expires = DateTime.UtcNow.AddSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2, p3, p4) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2 + p3 + p4); + }); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1, 3, 2)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(7, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsingAbsoluteExpirationOfTenSeconds() + { + var expires = DateTime.UtcNow.AddSeconds(10); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2, p3, p4, p5) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2 + p3 + p4 + p5); + }); + var rs = _cache.Memoize(expires, value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1, 3, 2, 4)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(11, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingDependencyExpirationOfTenSeconds() + { + var expires = new Func(() => new CountdownDependency(TimeSpan.FromSeconds(10))); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func(ExpensiveRandomString); + var rs = _cache.Memoize(expires(), value); + var sw = Stopwatch.StartNew(); + values.Add(rs()); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(17, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingDependencyExpirationOfTenSeconds() + { + var expires = new Func(() => new CountdownDependency(TimeSpan.FromSeconds(10))); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func(p1 => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1); + }); + var rs = _cache.Memoize(expires(), value); + var sw = Stopwatch.StartNew(); + values.Add(rs(3)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(3, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingDependencyExpirationOfTenSeconds() + { + var expires = new Func(() => new CountdownDependency(TimeSpan.FromSeconds(10))); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2); + }); + var rs = _cache.Memoize(expires(), value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(2, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsingDependencyExpirationOfTenSeconds() + { + var expires = new Func(() => new CountdownDependency(TimeSpan.FromSeconds(10))); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2, p3) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2 + p3); + }); + var rs = _cache.Memoize(expires(), value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1, 3)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(5, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsingDependencyExpirationOfTenSeconds() + { + var expires = new Func(() => new CountdownDependency(TimeSpan.FromSeconds(10))); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2, p3, p4) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2 + p3 + p4); + }); + var rs = _cache.Memoize(expires(), value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1, 3, 2)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(7, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + [Fact] + public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsingDependencyExpirationOfTenSeconds() + { + var expires = new Func(() => new CountdownDependency(TimeSpan.FromSeconds(10))); + var timeSpans = new ConcurrentBag(); + var values = new ConcurrentBag(); + + // we use Parallel because we want to assure thread safety of the extension method + Parallel.For(0, 25000, i => + { + var value = new Func((p1, p2, p3, p4, p5) => + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(p1 + p2 + p3 + p4 + p5); + }); + var rs = _cache.Memoize(expires(), value); + var sw = Stopwatch.StartNew(); + values.Add(rs(1, 1, 3, 2, 4)); + sw.Stop(); + timeSpans.Add(sw.Elapsed); + }); + + var s = Assert.Single(values.Distinct()); + Assert.Equal(11, s.Length); + Assert.True(Condition.IsPrime(s.Length)); + + var turtle = timeSpans.Where(ts => ts > TimeSpan.FromSeconds(1)).ToList(); + var rabbit = timeSpans.Where(ts => ts < TimeSpan.FromSeconds(1)).ToList(); + + foreach (var writeLockHit in turtle) + { + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + } + + TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); + TestOutput.WriteLine($"The rest, {rabbit.Count}, had {rabbit.Count(ts => ts < TimeSpan.FromMilliseconds(25))} in expected range (<25ms)."); + TestOutput.WriteLine(s); + + foreach (var nonLockHit in rabbit) + { + Assert.InRange(nonLockHit, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + + Thread.Sleep(TimeSpan.FromSeconds(10)); + + Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); + } + + private string ExpensiveRandomString() + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + return Generate.RandomString(17); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Runtime.Caching.Tests/Cuemon.Extensions.Runtime.Caching.Tests.csproj b/test/Cuemon.Extensions.Runtime.Caching.Tests/Cuemon.Extensions.Runtime.Caching.Tests.csproj new file mode 100644 index 000000000..e9e35eb08 --- /dev/null +++ b/test/Cuemon.Extensions.Runtime.Caching.Tests/Cuemon.Extensions.Runtime.Caching.Tests.csproj @@ -0,0 +1,12 @@ + + + + Cuemon.Extensions.Runtime.Caching + + + + + + + + \ No newline at end of file From 3c78a04d920f7c2d38571f3bfa08cda71ce4b056 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 17:18:43 +0200 Subject: [PATCH 200/385] Updated Nuget description and package tags. --- .../Cuemon.Extensions.Runtime.Caching.csproj | 4 ++-- src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj b/src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj index 274afe443..e2657efeb 100644 --- a/src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj +++ b/src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj @@ -8,8 +8,8 @@ Cuemon.Extensions.Runtime.Caching Cuemon.Extensions.Runtime.Caching - The Cuemon.Extensions.Runtime.Caching namespace contains extension methods and features that greatly complements the Cuemon.Runtime.Caching namespace. - extension-methods extensions memoization get-or-add + The Cuemon.Extensions.Runtime.Caching namespace contains extension methods that complements the Cuemon.Runtime.Caching namespace by adding support for Memoization techniques and GetOrAdd convenience; both with vast overloads and extended by the ICacheEnumerable{TKey} interface for loose coupling. + extension-methods extensions memoization memoize get-or-add thread-safe caching diff --git a/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj b/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj index 2a03fd0a3..81c0308a7 100644 --- a/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj +++ b/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -8,8 +8,8 @@ Cuemon.Runtime.Caching Cuemon.Runtime.Caching - The Cuemon.Runtime.Caching namespace contains features related to the System.Runtime.Caching namespace. - caching-manager + The Cuemon.Runtime.Caching namespace contains types related to interfaces for generic caching in applications while providing a concrete in-memory cache implementation named SlimMemoryCache. The namespace is an addition to the System.Runtime.Caching namespace. + i-cache-enumerable slim-memory-cache cache-entry cache-invalidation thread-safe From 1e5d4be4bebdccac4bc64a6a1aca85211f137f9b Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 18:54:25 +0200 Subject: [PATCH 201/385] Adjusted InRange for ADO pipelines. --- .../CacheEnumerableExtensionsTest.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs b/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs index acd7a9c77..6ada1741c 100644 --- a/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs +++ b/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs @@ -74,7 +74,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingSlidingExpirationOf foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -124,7 +124,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingS foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -174,7 +174,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingS foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -224,7 +224,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsin foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -274,7 +274,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsing foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -324,7 +324,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsing foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -370,7 +370,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingAbsoluteExpirationO foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -420,7 +420,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingA foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -470,7 +470,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingA foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -520,7 +520,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsin foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -570,7 +570,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsing foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -620,7 +620,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsing foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -666,7 +666,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingDependencyExpiratio foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -716,7 +716,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingD foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -766,7 +766,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingD foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -816,7 +816,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsin foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -866,7 +866,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsing foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); @@ -916,7 +916,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsing foreach (var writeLockHit in turtle) { - Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); + Assert.InRange(writeLockHit, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); } TestOutput.WriteLine($"Suspected hit-rate of PadLock: {turtle.Count}."); From 8b1deda5b45ec7f729b2a5a37f69aeed5e2d15ed Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 19:25:17 +0200 Subject: [PATCH 202/385] Updated Nuget package release notes and DocFx namespace documentation. --- .../Cuemon.Extensions.Runtime.Caching.md | 9 +++++++++ docfx/api/namespaces/Cuemon.Runtime.Caching.md | 6 +++++- .../Properties/PackageReleaseNotes.txt | 5 +++++ .../Properties/PackageReleaseNotes.txt | 18 ++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md create mode 100644 src/Cuemon.Extensions.Runtime.Caching/Properties/PackageReleaseNotes.txt create mode 100644 src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md b/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md new file mode 100644 index 000000000..bf69ac741 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md @@ -0,0 +1,9 @@ +--- +uid: Cuemon.Extensions.Runtime.Caching +summary: *content +--- +The Cuemon.Extensions.Runtime.Caching namespace contains extension methods that complements the Cuemon.Runtime.Caching namespace by adding support for Memoization techniques and GetOrAdd convenience; both with vast overloads and extended by the ICacheEnumerable{TKey} interface for loose coupling. + +Availability: NET Standard 2.0 + +Complements: [Cuemon.Runtime.Caching namespace](hhttps://docs.cuemon.net/api/dotnet/Cuemon.Runtime.Caching.html) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Runtime.Caching.md b/docfx/api/namespaces/Cuemon.Runtime.Caching.md index ef3a5cee2..163e5360d 100644 --- a/docfx/api/namespaces/Cuemon.Runtime.Caching.md +++ b/docfx/api/namespaces/Cuemon.Runtime.Caching.md @@ -2,4 +2,8 @@ uid: Cuemon.Runtime.Caching summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Runtime.Caching namespace contains types related to interfaces for generic caching in applications while providing a concrete in-memory cache implementation named SlimMemoryCache. The namespace is an addition to the System.Runtime.Caching namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Runtime.Caching namespace](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.caching?view=netframework-4.6.1) \ No newline at end of file diff --git a/src/Cuemon.Extensions.Runtime.Caching/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Runtime.Caching/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..3805f65e4 --- /dev/null +++ b/src/Cuemon.Extensions.Runtime.Caching/Properties/PackageReleaseNotes.txt @@ -0,0 +1,5 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED CacheEnumerableExtensions class in the Cuemon.Extensions.Runtime.Caching namespace that consist of extension methods for the ICacheEnumerable{TKey} interface: GetOrAdd, Memoize \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt b/src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..85d9100bf --- /dev/null +++ b/src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt @@ -0,0 +1,18 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# Upgrade Steps +- Some features (such as Memoization techniques and GetOrAdd convenience) was moved to the Cuemon.Extensions.Runtime.Caching namespace as extension methods (to keep the ICacheEnumerable{TKey} slim) +- The extent of refactoring applied to this project resulted in so many breaking changes that a git diff is advisable +  +# New Features +- ADDED CacheEntry class in the Cuemon.Runtime.Caching namespace that represents an individual cache entry in the cache +- ADDED CacheInvalidation class in the Cuemon.Runtime.Caching namespace that represents a set of eviction and expiration details for a specific cache entry +- ADDED ICacheEnumerable{TKey} interface in the Cuemon.Runtime.Caching namespace that is used to provide cache implementations for an application +- ADDED SlimMemoryCache class in the Cuemon.Runtime.Caching namespace that represents the type that implements an in-memory cache for an application +- ADDED SlimMemoryCacheOptions class in the Cuemon.Runtime.Caching namespace that specifies options related to SlimMemoryCache +  +# Breaking Changes +- CHANGED CachingManager class to return a singleton of SlimMemoryCache with default options (kept for legacy and convenience) +- REPLACED Cache class in the Cuemon.Runtime.Caching namespace with CacheEntry and split the cache invalidation part into its own class; CacheInvalidation +- REPLACED CacheCollection class in the Cuemon.Runtime.Caching namespace with SlimMemoryCache \ No newline at end of file From f85cdbb96432404da6de90c9122701577adc9f56 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 19:35:07 +0200 Subject: [PATCH 203/385] Added justification for code quality S2436. --- .../GlobalSuppressions.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/Cuemon.Extensions.Runtime.Caching/GlobalSuppressions.cs diff --git a/src/Cuemon.Extensions.Runtime.Caching/GlobalSuppressions.cs b/src/Cuemon.Extensions.Runtime.Caching/GlobalSuppressions.cs new file mode 100644 index 000000000..b17e8ffe3 --- /dev/null +++ b/src/Cuemon.Extensions.Runtime.Caching/GlobalSuppressions.cs @@ -0,0 +1,27 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``4(Cuemon.Runtime.Caching.ICacheEnumerable{``0},Cuemon.Runtime.Caching.CacheInvalidation,System.Func{``1,``2,``3})~System.Func{``1,``2,``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``4(Cuemon.Runtime.Caching.ICacheEnumerable{``0},Cuemon.Runtime.IDependency,System.Func{``1,``2,``3})~System.Func{``1,``2,``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``4(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.Collections.Generic.IEnumerable{Cuemon.Runtime.IDependency},System.Func{``1,``2,``3})~System.Func{``1,``2,``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``4(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.DateTime,System.Func{``1,``2,``3})~System.Func{``1,``2,``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``4(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.TimeSpan,System.Func{``1,``2,``3})~System.Func{``1,``2,``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``5(Cuemon.Runtime.Caching.ICacheEnumerable{``0},Cuemon.Runtime.Caching.CacheInvalidation,System.Func{``1,``2,``3,``4})~System.Func{``1,``2,``3,``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``5(Cuemon.Runtime.Caching.ICacheEnumerable{``0},Cuemon.Runtime.IDependency,System.Func{``1,``2,``3,``4})~System.Func{``1,``2,``3,``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``5(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.Collections.Generic.IEnumerable{Cuemon.Runtime.IDependency},System.Func{``1,``2,``3,``4})~System.Func{``1,``2,``3,``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``5(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.DateTime,System.Func{``1,``2,``3,``4})~System.Func{``1,``2,``3,``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``5(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.TimeSpan,System.Func{``1,``2,``3,``4})~System.Func{``1,``2,``3,``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``6(Cuemon.Runtime.Caching.ICacheEnumerable{``0},Cuemon.Runtime.Caching.CacheInvalidation,System.Func{``1,``2,``3,``4,``5})~System.Func{``1,``2,``3,``4,``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``6(Cuemon.Runtime.Caching.ICacheEnumerable{``0},Cuemon.Runtime.IDependency,System.Func{``1,``2,``3,``4,``5})~System.Func{``1,``2,``3,``4,``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``6(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.Collections.Generic.IEnumerable{Cuemon.Runtime.IDependency},System.Func{``1,``2,``3,``4,``5})~System.Func{``1,``2,``3,``4,``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``6(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.DateTime,System.Func{``1,``2,``3,``4,``5})~System.Func{``1,``2,``3,``4,``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``6(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.TimeSpan,System.Func{``1,``2,``3,``4,``5})~System.Func{``1,``2,``3,``4,``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``7(Cuemon.Runtime.Caching.ICacheEnumerable{``0},Cuemon.Runtime.Caching.CacheInvalidation,System.Func{``1,``2,``3,``4,``5,``6})~System.Func{``1,``2,``3,``4,``5,``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``7(Cuemon.Runtime.Caching.ICacheEnumerable{``0},Cuemon.Runtime.IDependency,System.Func{``1,``2,``3,``4,``5,``6})~System.Func{``1,``2,``3,``4,``5,``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``7(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.Collections.Generic.IEnumerable{Cuemon.Runtime.IDependency},System.Func{``1,``2,``3,``4,``5,``6})~System.Func{``1,``2,``3,``4,``5,``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``7(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.DateTime,System.Func{``1,``2,``3,``4,``5,``6})~System.Func{``1,``2,``3,``4,``5,``6}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 generic arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Extensions.Runtime.Caching.CacheEnumerableExtensions.Memoize``7(Cuemon.Runtime.Caching.ICacheEnumerable{``0},System.TimeSpan,System.Func{``1,``2,``3,``4,``5,``6})~System.Func{``1,``2,``3,``4,``5,``6}")] From 8a3635035b71214e4edd7970d1bec7c56d621c03 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 19:41:46 +0200 Subject: [PATCH 204/385] Included Cuemon.Extensions.Runtime.Caching in DocFx. --- docfx/docfx.json | 1 + 1 file changed, 1 insertion(+) diff --git a/docfx/docfx.json b/docfx/docfx.json index 2cc08dd2c..ff114a353 100644 --- a/docfx/docfx.json +++ b/docfx/docfx.json @@ -41,6 +41,7 @@ "Cuemon.Extensions.Net/**.csproj", "Cuemon.Extensions.Newtonsoft.Json/**.csproj", "Cuemon.Extensions.Reflection/**.csproj", + "Cuemon.Extensions.Runtime.Caching/**.csproj", "Cuemon.Extensions.Text/**.csproj", "Cuemon.Extensions.Threading/**.csproj", "Cuemon.Extensions.Xml/**.csproj", From 6079bd32c492aeae1a8a5b0ae4e952ef88242edf Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 19:43:55 +0200 Subject: [PATCH 205/385] Fixed hhttps -> https. --- docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md b/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md index bf69ac741..3aa8df1f9 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md @@ -6,4 +6,4 @@ The Cuemon.Extensions.Runtime.Caching namespace contains extension methods that Availability: NET Standard 2.0 -Complements: [Cuemon.Runtime.Caching namespace](hhttps://docs.cuemon.net/api/dotnet/Cuemon.Runtime.Caching.html) \ No newline at end of file +Complements: [Cuemon.Runtime.Caching namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Runtime.Caching.html) \ No newline at end of file From 695b4ca2ba679e12c16102cfb02531cf947f7865 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 21:26:37 +0200 Subject: [PATCH 206/385] Reduced number of parallel items from 5000/25000 to 1000 (random fail on ADO; works fine both from VS and in local Docker env). --- .../CacheEnumerableExtensionsTest.cs | 38 +++++++++---------- .../SlimMemoryCacheTest.cs | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs b/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs index 6ada1741c..c86525742 100644 --- a/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs +++ b/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs @@ -26,7 +26,7 @@ public CacheEnumerableExtensionsTest(HostFixture hostFixture, ITestOutputHelper [Fact] public void GetOrAdd_ShouldCacheAndReturnItemInOneGoUsingSlidingExpirationOfTenSeconds() { - var items = 25000; + var items = 1000; var expires = TimeSpan.FromSeconds(10); var keys = Generate.RangeOf(items, i => Guid.NewGuid().ToString("N")).ToList(); var bag = new ConcurrentBag(); @@ -55,7 +55,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingSlidingExpirationOf var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func(ExpensiveRandomString); var rs = _cache.Memoize(expires, value); @@ -101,7 +101,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingS var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func(p1 => { @@ -151,7 +151,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingS var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2) => { @@ -201,7 +201,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsin var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2, p3) => { @@ -251,7 +251,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsing var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2, p3, p4) => { @@ -301,7 +301,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsing var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2, p3, p4, p5) => { @@ -351,7 +351,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingAbsoluteExpirationO var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func(ExpensiveRandomString); var rs = _cache.Memoize(expires, value); @@ -397,7 +397,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingA var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func(p1 => { @@ -447,7 +447,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingA var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2) => { @@ -497,7 +497,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsin var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2, p3) => { @@ -547,7 +547,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsing var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2, p3, p4) => { @@ -597,7 +597,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsing var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2, p3, p4, p5) => { @@ -647,7 +647,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingDependencyExpiratio var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func(ExpensiveRandomString); var rs = _cache.Memoize(expires(), value); @@ -693,7 +693,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingD var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func(p1 => { @@ -743,7 +743,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingD var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2) => { @@ -793,7 +793,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsin var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2, p3) => { @@ -843,7 +843,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsing var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2, p3, p4) => { @@ -893,7 +893,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsing var values = new ConcurrentBag(); // we use Parallel because we want to assure thread safety of the extension method - Parallel.For(0, 25000, i => + Parallel.For(0, 1000, i => { var value = new Func((p1, p2, p3, p4, p5) => { diff --git a/test/Cuemon.Runtime.Caching.Tests/SlimMemoryCacheTest.cs b/test/Cuemon.Runtime.Caching.Tests/SlimMemoryCacheTest.cs index b511d7cf1..1a8c70801 100644 --- a/test/Cuemon.Runtime.Caching.Tests/SlimMemoryCacheTest.cs +++ b/test/Cuemon.Runtime.Caching.Tests/SlimMemoryCacheTest.cs @@ -25,7 +25,7 @@ public class SlimMemoryCacheTest : HostTest private const string Absolute60Namespace = "Absolute60"; private const string Dependency60Namespace = "Dependency60"; - private const int NumberOfItemsToCache = 5000; + private const int NumberOfItemsToCache = 1000; public SlimMemoryCacheTest(HostFixture hostFixture, ITestOutputHelper output = null) : base(hostFixture, output) { From f10b3a9b34d0b3cabce0c60d2304747ff2e9b1fe Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 21:32:56 +0200 Subject: [PATCH 207/385] Moved SafeInvoke and SafeInvokeAsync from Disposable to Patterns. Reason is confusing inheritance hierarchy in DocFx .. and Patterns class might be a better fit. --- src/Cuemon.Core/Disposable.cs | 334 ---------------- .../ByteArrayDecoratorExtensions.cs | 4 +- .../Extensions/StringDecoratorExtensions.cs | 4 +- src/Cuemon.Core/GlobalSuppressions.cs | 18 +- src/Cuemon.Core/Patterns.cs | 362 +++++++++++++++++- src/Cuemon.Core/Security/Hash.cs | 2 +- src/Cuemon.Core/Text/ByteOrderMark.cs | 2 +- src/Cuemon.Extensions.Xml/StreamExtensions.cs | 4 +- .../Extensions/StreamDecoratorExtensions.cs | 8 +- src/Cuemon.IO/StreamFactory.cs | 2 +- src/Cuemon.Net/NetDependency.cs | 2 +- .../AesCryptor.cs | 2 +- src/Cuemon.Xml/XmlStreamFactory.cs | 2 +- test/Cuemon.Core.Tests/DisposableTest.cs | 14 +- 14 files changed, 381 insertions(+), 379 deletions(-) diff --git a/src/Cuemon.Core/Disposable.cs b/src/Cuemon.Core/Disposable.cs index aa9ae86dd..30b797125 100644 --- a/src/Cuemon.Core/Disposable.cs +++ b/src/Cuemon.Core/Disposable.cs @@ -11,340 +11,6 @@ namespace Cuemon /// public abstract class Disposable : IDisposable { - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The delegate that will handle any exceptions might thrown by . - /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static TResult SafeInvoke(Func initializer, Func tester, Action catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = FuncFactory.Create(tester, default); - var f2 = ActionFactory.Create(catcher, default); - return SafeInvokeCore(f1, initializer, f2); - } - - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the parameter of the function delegate and delegate . - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The parameter of the function delegate and delegate . - /// The delegate that will handle any exceptions might thrown by . - /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static TResult SafeInvoke(Func initializer, Func tester, T arg, Action catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = FuncFactory.Create(tester, default, arg); - var f2 = ActionFactory.Create(catcher, default, arg); - return SafeInvokeCore(f1, initializer, f2); - } - - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the first parameter of the function delegate and delegate . - /// The type of the second parameter of the function delegate and delegate . - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The first parameter of the function delegate and delegate . - /// The second parameter of the function delegate and delegate . - /// The delegate that will handle any exceptions might thrown by . - /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static TResult SafeInvoke(Func initializer, Func tester, T1 arg1, T2 arg2, Action catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = FuncFactory.Create(tester, default, arg1, arg2); - var f2 = ActionFactory.Create(catcher, default, arg1, arg2); - return SafeInvokeCore(f1, initializer, f2); - } - - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the first parameter of the function delegate and delegate . - /// The type of the second parameter of the function delegate and delegate . - /// The type of the third parameter of the function delegate and delegate . - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The first parameter of the function delegate and delegate . - /// The second parameter of the function delegate and delegate . - /// The third parameter of the function delegate and delegate . - /// The delegate that will handle any exceptions might thrown by . - /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static TResult SafeInvoke(Func initializer, Func tester, T1 arg1, T2 arg2, T3 arg3, Action catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = FuncFactory.Create(tester, default, arg1, arg2, arg3); - var f2 = ActionFactory.Create(catcher, default, arg1, arg2, arg3); - return SafeInvokeCore(f1, initializer, f2); - } - - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the first parameter of the function delegate and delegate . - /// The type of the second parameter of the function delegate and delegate . - /// The type of the third parameter of the function delegate and delegate . - /// The type of the fourth parameter of the function delegate and delegate . - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The first parameter of the function delegate and delegate . - /// The second parameter of the function delegate and delegate . - /// The third parameter of the function delegate and delegate . - /// The fourth parameter of the function delegate and delegate . - /// The delegate that will handle any exceptions might thrown by . - /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static TResult SafeInvoke(Func initializer, Func tester, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = FuncFactory.Create(tester, default, arg1, arg2, arg3, arg4); - var f2 = ActionFactory.Create(catcher, default, arg1, arg2, arg3, arg4); - return SafeInvokeCore(f1, initializer, f2); - } - - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the first parameter of the function delegate and delegate . - /// The type of the second parameter of the function delegate and delegate . - /// The type of the third parameter of the function delegate and delegate . - /// The type of the fourth parameter of the function delegate and delegate . - /// The type of the fifth parameter of the function delegate and delegate . - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The first parameter of the function delegate and delegate . - /// The second parameter of the function delegate and delegate . - /// The third parameter of the function delegate and delegate . - /// The fourth parameter of the function delegate and delegate . - /// The fifth parameter of the function delegate and delegate . - /// The delegate that will handle any exceptions might thrown by . - /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static TResult SafeInvoke(Func initializer, Func tester, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = FuncFactory.Create(tester, default, arg1, arg2, arg3, arg4, arg5); - var f2 = ActionFactory.Create(catcher, default, arg1, arg2, arg3, arg4, arg5); - return SafeInvokeCore(f1, initializer, f2); - } - - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The token to monitor for cancellation requests. The default value is . - /// The function delegate that will handle any exceptions might thrown by . - /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static Task SafeInvokeAsync(Func initializer, Func> tester, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = TaskFuncFactory.Create(tester, default); - var f2 = TaskActionFactory.Create(catcher, default); - return SafeInvokeAsyncCore(f1, initializer, f2, ct); - } - - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the parameter of the function delegate and delegate . - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The parameter of the function delegate and delegate . - /// The token to monitor for cancellation requests. The default value is . - /// The function delegate that will handle any exceptions might thrown by . - /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static Task SafeInvokeAsync(Func initializer, Func> tester, T arg, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = TaskFuncFactory.Create(tester, default, arg); - var f2 = TaskActionFactory.Create(catcher, default, arg); - return SafeInvokeAsyncCore(f1, initializer, f2, ct); - } - - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the first parameter of the function delegate and delegate . - /// The type of the second parameter of the function delegate and delegate . - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The first parameter of the function delegate and delegate . - /// The second parameter of the function delegate and delegate . - /// The token to monitor for cancellation requests. The default value is . - /// The function delegate that will handle any exceptions might thrown by . - /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static Task SafeInvokeAsync(Func initializer, Func> tester, T1 arg1, T2 arg2, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = TaskFuncFactory.Create(tester, default, arg1, arg2); - var f2 = TaskActionFactory.Create(catcher, default, arg1, arg2); - return SafeInvokeAsyncCore(f1, initializer, f2, ct); - } - - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the first parameter of the function delegate and delegate . - /// The type of the second parameter of the function delegate and delegate . - /// The type of the third parameter of the function delegate and delegate . - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The first parameter of the function delegate and delegate . - /// The second parameter of the function delegate and delegate . - /// The third parameter of the function delegate and delegate . - /// The token to monitor for cancellation requests. The default value is . - /// The function delegate that will handle any exceptions might thrown by . - /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static Task SafeInvokeAsync(Func initializer, Func> tester, T1 arg1, T2 arg2, T3 arg3, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = TaskFuncFactory.Create(tester, default, arg1, arg2, arg3); - var f2 = TaskActionFactory.Create(catcher, default, arg1, arg2, arg3); - return SafeInvokeAsyncCore(f1, initializer, f2, ct); - } - - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the first parameter of the function delegate and delegate . - /// The type of the second parameter of the function delegate and delegate . - /// The type of the third parameter of the function delegate and delegate . - /// The type of the fourth parameter of the function delegate and delegate . - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The first parameter of the function delegate and delegate . - /// The second parameter of the function delegate and delegate . - /// The third parameter of the function delegate and delegate . - /// The fourth parameter of the function delegate and delegate . - /// The token to monitor for cancellation requests. The default value is . - /// The function delegate that will handle any exceptions might thrown by . - /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static Task SafeInvokeAsync(Func initializer, Func> tester, T1 arg1, T2 arg2, T3 arg3, T4 arg4, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = TaskFuncFactory.Create(tester, default, arg1, arg2, arg3, arg4); - var f2 = TaskActionFactory.Create(catcher, default, arg1, arg2, arg3, arg4); - return SafeInvokeAsyncCore(f1, initializer, f2, ct); - } - - /// - /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). - /// - /// The type of the first parameter of the function delegate and delegate . - /// The type of the second parameter of the function delegate and delegate . - /// The type of the third parameter of the function delegate and delegate . - /// The type of the fourth parameter of the function delegate and delegate . - /// The type of the fifth parameter of the function delegate and delegate . - /// The type of the return value of the function delegate . - /// The function delegate that initializes an object implementing the interface. - /// The function delegate that is used to ensure that operations performed on abides CA2000. - /// The first parameter of the function delegate and delegate . - /// The second parameter of the function delegate and delegate . - /// The third parameter of the function delegate and delegate . - /// The fourth parameter of the function delegate and delegate . - /// The fifth parameter of the function delegate and delegate . - /// The token to monitor for cancellation requests. The default value is . - /// The function delegate that will handle any exceptions might thrown by . - /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. - public static Task SafeInvokeAsync(Func initializer, Func> tester, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable - { - Validator.ThrowIfNull(initializer, nameof(initializer)); - Validator.ThrowIfNull(tester, nameof(tester)); - var f1 = TaskFuncFactory.Create(tester, default, arg1, arg2, arg3, arg4, arg5); - var f2 = TaskActionFactory.Create(catcher, default, arg1, arg2, arg3, arg4, arg5); - return SafeInvokeAsyncCore(f1, initializer, f2, ct); - } - - private static TResult SafeInvokeCore(FuncFactory testerFactory, Func initializer, ActionFactory catcherFactory) - where TResult : class, IDisposable - where TTester : Template - where TCatcher : Template - { - TResult result = null; - try - { - testerFactory.GenericArguments.Arg1 = initializer(); - testerFactory.GenericArguments.Arg1 = testerFactory.ExecuteMethod(); - result = testerFactory.GenericArguments.Arg1; - testerFactory.GenericArguments.Arg1 = null; - } - catch (Exception e) - { - if (!catcherFactory.HasDelegate) - { - throw; - } - else - { - catcherFactory.GenericArguments.Arg1 = e; - catcherFactory.ExecuteMethod(); - } - } - finally - { - testerFactory.GenericArguments.Arg1?.Dispose(); - } - return result; - } - - private static async Task SafeInvokeAsyncCore(TaskFuncFactory testerFactory, Func initializer, TaskActionFactory catcherFactory, CancellationToken ct) - where TResult : class, IDisposable - where TTester : Template - where TCatcher : Template - { - TResult result = null; - try - { - testerFactory.GenericArguments.Arg1 = initializer(); - testerFactory.GenericArguments.Arg1 = await testerFactory.ExecuteMethodAsync(ct).ConfigureAwait(false); - result = testerFactory.GenericArguments.Arg1; - testerFactory.GenericArguments.Arg1 = null; - } - catch (Exception e) - { - if (!catcherFactory.HasDelegate) - { - throw; - } - else - { - catcherFactory.GenericArguments.Arg1 = e; - await catcherFactory.ExecuteMethodAsync(ct).ConfigureAwait(false); - } - } - finally - { - testerFactory.GenericArguments.Arg1?.Dispose(); - } - return result; - } - /// /// Gets a value indicating whether this object is disposed. /// diff --git a/src/Cuemon.Core/Extensions/ByteArrayDecoratorExtensions.cs b/src/Cuemon.Core/Extensions/ByteArrayDecoratorExtensions.cs index af38a2f93..894b279b5 100644 --- a/src/Cuemon.Core/Extensions/ByteArrayDecoratorExtensions.cs +++ b/src/Cuemon.Core/Extensions/ByteArrayDecoratorExtensions.cs @@ -37,7 +37,7 @@ public static string ToEncodedString(this IDecorator decorator, Action decorator) { Validator.ThrowIfNull(decorator, nameof(decorator)); - return Disposable.SafeInvoke(() => new MemoryStream(decorator.Inner.Length), ms => + return Patterns.SafeInvoke(() => new MemoryStream(decorator.Inner.Length), ms => { ms.Write(decorator.Inner, 0, decorator.Inner.Length); ms.Position = 0; @@ -57,7 +57,7 @@ public static Stream ToStream(this IDecorator decorator) public static Task ToStreamAsync(this IDecorator decorator, CancellationToken ct = default) { Validator.ThrowIfNull(decorator, nameof(decorator)); - return Disposable.SafeInvokeAsync(() => new MemoryStream(decorator.Inner.Length), async (ms, cti) => + return Patterns.SafeInvokeAsync(() => new MemoryStream(decorator.Inner.Length), async (ms, cti) => { await ms.WriteAsync(decorator.Inner, 0, decorator.Inner.Length, cti).ConfigureAwait(false); ms.Position = 0; diff --git a/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs b/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs index 024731f1b..6ab101613 100644 --- a/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs +++ b/src/Cuemon.Core/Extensions/StringDecoratorExtensions.cs @@ -140,7 +140,7 @@ public static string ToAsciiEncodedString(this IDecorator decorator, Act public static Stream ToStream(this IDecorator decorator, Action setup = null) { Validator.ThrowIfNull(decorator, nameof(decorator)); - return Disposable.SafeInvoke(() => new MemoryStream(), ms => + return Patterns.SafeInvoke(() => new MemoryStream(), ms => { var bytes = Convertible.GetBytes(decorator.Inner, setup); ms.Write(bytes, 0, bytes.Length); @@ -166,7 +166,7 @@ public static Stream ToStream(this IDecorator decorator, Action ToStreamAsync(this IDecorator decorator, CancellationToken ct = default, Action setup = null) { Validator.ThrowIfNull(decorator, nameof(decorator)); - return Disposable.SafeInvokeAsync(() => new MemoryStream(), async (ms, token) => + return Patterns.SafeInvokeAsync(() => new MemoryStream(), async (ms, token) => { var bytes = Convertible.GetBytes(decorator.Inner, setup); await ms.WriteAsync(bytes, 0, bytes.Length, token).ConfigureAwait(false); diff --git a/src/Cuemon.Core/GlobalSuppressions.cs b/src/Cuemon.Core/GlobalSuppressions.cs index 1f79ad696..9d0d5e37c 100644 --- a/src/Cuemon.Core/GlobalSuppressions.cs +++ b/src/Cuemon.Core/GlobalSuppressions.cs @@ -238,15 +238,15 @@ [assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``9(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7,``8}")] [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "member", Target = "~M:Cuemon.TesterFuncFactory.Create``9(Cuemon.TesterFunc{``0,``1,``2,``3,``4,``5,``6,``7,``8},``0,``1,``2,``3,``4,``5,``6)~Cuemon.TesterFuncFactory{Cuemon.Template{``0,``1,``2,``3,``4,``5,``6},``7,``8}")] [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; think about it as a Tuple/Variadic - but for TesterFunc delegates.", Scope = "type", Target = "~T:Cuemon.TesterFuncFactory`3")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] -[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``4(System.Func{``3},System.Func{``3,``0,``1,``2,``3},``0,``1,``2,System.Action{System.Exception,``0,``1,``2})~``3")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{System.Exception,``0,``1,``2,``3})~``4")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``4(System.Func{``3},System.Func{``3,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``3}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] -[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Disposable.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Patterns.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Patterns.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Patterns.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Patterns.SafeInvoke``4(System.Func{``3},System.Func{``3,``0,``1,``2,``3},``0,``1,``2,System.Action{System.Exception,``0,``1,``2})~``3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Patterns.SafeInvoke``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,``4},``0,``1,``2,``3,System.Action{System.Exception,``0,``1,``2,``3})~``4")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Patterns.SafeInvoke``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,``5},``0,``1,``2,``3,``4,System.Action{System.Exception,``0,``1,``2,``3,``4})~``5")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Patterns.SafeInvokeAsync``4(System.Func{``3},System.Func{``3,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task{``3}},``0,``1,``2,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``3}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Patterns.SafeInvokeAsync``5(System.Func{``4},System.Func{``4,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task{``4}},``0,``1,``2,``3,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``4}")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions)..", Scope = "member", Target = "~M:Cuemon.Patterns.SafeInvokeAsync``6(System.Func{``5},System.Func{``5,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task{``5}},``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Func{System.Exception,``0,``1,``2,``3,``4,System.Threading.CancellationToken,System.Threading.Tasks.Task})~System.Threading.Tasks.Task{``5}")] [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``4(System.Boolean,System.Action{``0,``1,``2,``3},System.Action{``0,``1,``2,``3},``0,``1,``2,``3)")] [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``5(System.Boolean,System.Action{``0,``1,``2,``3,``4},System.Action{``0,``1,``2,``3,``4},``0,``1,``2,``3,``4)")] [assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Condition.FlipFlop``5(System.Boolean,System.Action{``0,``1,``2,``3,``4},System.Action{``0,``1,``2,``3,``4},``0,``1,``2,``3,``4)")] diff --git a/src/Cuemon.Core/Patterns.cs b/src/Cuemon.Core/Patterns.cs index 18f97cc22..56511492d 100644 --- a/src/Cuemon.Core/Patterns.cs +++ b/src/Cuemon.Core/Patterns.cs @@ -2,11 +2,13 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; namespace Cuemon { /// - /// Provides a generic way to support different types of design patterns with small utility methods. + /// Provides a generic way to support different types of design patterns and practices with small utility methods. /// public sealed class Patterns { @@ -34,13 +36,13 @@ public static bool TryInvoke(Action method) } catch (Exception ex) { - if (ex is OutOfMemoryException || - ex is StackOverflowException || - ex is SEHException || - ex is AccessViolationException || - #pragma warning disable CS0618 // Type or member is obsolete + if (ex is OutOfMemoryException || + ex is StackOverflowException || + ex is SEHException || + ex is AccessViolationException || +#pragma warning disable CS0618 // Type or member is obsolete ex is ExecutionEngineException) // fatal exceptions; re-throw for .NET "legacy" (.NET Core will handle these by a high-level catch-all handler) - #pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore CS0618 // Type or member is obsolete { throw; } @@ -66,13 +68,13 @@ public static bool TryInvoke(Func method, out TResult result) } catch (Exception ex) { - if (ex is OutOfMemoryException || - ex is StackOverflowException || - ex is SEHException || + if (ex is OutOfMemoryException || + ex is StackOverflowException || + ex is SEHException || ex is AccessViolationException || - #pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable CS0618 // Type or member is obsolete ex is ExecutionEngineException) // fatal exceptions; re-throw for .NET "legacy" (.NET Core will handle these by a high-level catch-all handler) - #pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore CS0618 // Type or member is obsolete { throw; } @@ -166,9 +168,343 @@ public static Action ConfigureRevert(TOptions options) return o => { var to = typeof(TOptions); - var tops= to.GetRuntimeProperties().Where(pi => pi.CanRead && pi.CanWrite).ToList(); + var tops = to.GetRuntimeProperties().Where(pi => pi.CanRead && pi.CanWrite).ToList(); foreach (var p in tops) { p.SetValue(o, p.GetValue(options)); } }; } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The delegate that will handle any exceptions might thrown by . + /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static TResult SafeInvoke(Func initializer, Func tester, Action catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = FuncFactory.Create(tester, default); + var f2 = ActionFactory.Create(catcher, default); + return SafeInvokeCore(f1, initializer, f2); + } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the parameter of the function delegate and delegate . + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The parameter of the function delegate and delegate . + /// The delegate that will handle any exceptions might thrown by . + /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static TResult SafeInvoke(Func initializer, Func tester, T arg, Action catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = FuncFactory.Create(tester, default, arg); + var f2 = ActionFactory.Create(catcher, default, arg); + return SafeInvokeCore(f1, initializer, f2); + } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the first parameter of the function delegate and delegate . + /// The type of the second parameter of the function delegate and delegate . + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The first parameter of the function delegate and delegate . + /// The second parameter of the function delegate and delegate . + /// The delegate that will handle any exceptions might thrown by . + /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static TResult SafeInvoke(Func initializer, Func tester, T1 arg1, T2 arg2, Action catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = FuncFactory.Create(tester, default, arg1, arg2); + var f2 = ActionFactory.Create(catcher, default, arg1, arg2); + return SafeInvokeCore(f1, initializer, f2); + } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the first parameter of the function delegate and delegate . + /// The type of the second parameter of the function delegate and delegate . + /// The type of the third parameter of the function delegate and delegate . + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The first parameter of the function delegate and delegate . + /// The second parameter of the function delegate and delegate . + /// The third parameter of the function delegate and delegate . + /// The delegate that will handle any exceptions might thrown by . + /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static TResult SafeInvoke(Func initializer, Func tester, T1 arg1, T2 arg2, T3 arg3, Action catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = FuncFactory.Create(tester, default, arg1, arg2, arg3); + var f2 = ActionFactory.Create(catcher, default, arg1, arg2, arg3); + return SafeInvokeCore(f1, initializer, f2); + } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the first parameter of the function delegate and delegate . + /// The type of the second parameter of the function delegate and delegate . + /// The type of the third parameter of the function delegate and delegate . + /// The type of the fourth parameter of the function delegate and delegate . + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The first parameter of the function delegate and delegate . + /// The second parameter of the function delegate and delegate . + /// The third parameter of the function delegate and delegate . + /// The fourth parameter of the function delegate and delegate . + /// The delegate that will handle any exceptions might thrown by . + /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static TResult SafeInvoke(Func initializer, Func tester, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Action catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = FuncFactory.Create(tester, default, arg1, arg2, arg3, arg4); + var f2 = ActionFactory.Create(catcher, default, arg1, arg2, arg3, arg4); + return SafeInvokeCore(f1, initializer, f2); + } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the first parameter of the function delegate and delegate . + /// The type of the second parameter of the function delegate and delegate . + /// The type of the third parameter of the function delegate and delegate . + /// The type of the fourth parameter of the function delegate and delegate . + /// The type of the fifth parameter of the function delegate and delegate . + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The first parameter of the function delegate and delegate . + /// The second parameter of the function delegate and delegate . + /// The third parameter of the function delegate and delegate . + /// The fourth parameter of the function delegate and delegate . + /// The fifth parameter of the function delegate and delegate . + /// The delegate that will handle any exceptions might thrown by . + /// The return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static TResult SafeInvoke(Func initializer, Func tester, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Action catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = FuncFactory.Create(tester, default, arg1, arg2, arg3, arg4, arg5); + var f2 = ActionFactory.Create(catcher, default, arg1, arg2, arg3, arg4, arg5); + return SafeInvokeCore(f1, initializer, f2); + } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The token to monitor for cancellation requests. The default value is . + /// The function delegate that will handle any exceptions might thrown by . + /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static Task SafeInvokeAsync(Func initializer, Func> tester, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = TaskFuncFactory.Create(tester, default); + var f2 = TaskActionFactory.Create(catcher, default); + return SafeInvokeAsyncCore(f1, initializer, f2, ct); + } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the parameter of the function delegate and delegate . + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The parameter of the function delegate and delegate . + /// The token to monitor for cancellation requests. The default value is . + /// The function delegate that will handle any exceptions might thrown by . + /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static Task SafeInvokeAsync(Func initializer, Func> tester, T arg, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = TaskFuncFactory.Create(tester, default, arg); + var f2 = TaskActionFactory.Create(catcher, default, arg); + return SafeInvokeAsyncCore(f1, initializer, f2, ct); + } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the first parameter of the function delegate and delegate . + /// The type of the second parameter of the function delegate and delegate . + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The first parameter of the function delegate and delegate . + /// The second parameter of the function delegate and delegate . + /// The token to monitor for cancellation requests. The default value is . + /// The function delegate that will handle any exceptions might thrown by . + /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static Task SafeInvokeAsync(Func initializer, Func> tester, T1 arg1, T2 arg2, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = TaskFuncFactory.Create(tester, default, arg1, arg2); + var f2 = TaskActionFactory.Create(catcher, default, arg1, arg2); + return SafeInvokeAsyncCore(f1, initializer, f2, ct); + } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the first parameter of the function delegate and delegate . + /// The type of the second parameter of the function delegate and delegate . + /// The type of the third parameter of the function delegate and delegate . + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The first parameter of the function delegate and delegate . + /// The second parameter of the function delegate and delegate . + /// The third parameter of the function delegate and delegate . + /// The token to monitor for cancellation requests. The default value is . + /// The function delegate that will handle any exceptions might thrown by . + /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static Task SafeInvokeAsync(Func initializer, Func> tester, T1 arg1, T2 arg2, T3 arg3, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = TaskFuncFactory.Create(tester, default, arg1, arg2, arg3); + var f2 = TaskActionFactory.Create(catcher, default, arg1, arg2, arg3); + return SafeInvokeAsyncCore(f1, initializer, f2, ct); + } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the first parameter of the function delegate and delegate . + /// The type of the second parameter of the function delegate and delegate . + /// The type of the third parameter of the function delegate and delegate . + /// The type of the fourth parameter of the function delegate and delegate . + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The first parameter of the function delegate and delegate . + /// The second parameter of the function delegate and delegate . + /// The third parameter of the function delegate and delegate . + /// The fourth parameter of the function delegate and delegate . + /// The token to monitor for cancellation requests. The default value is . + /// The function delegate that will handle any exceptions might thrown by . + /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static Task SafeInvokeAsync(Func initializer, Func> tester, T1 arg1, T2 arg2, T3 arg3, T4 arg4, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = TaskFuncFactory.Create(tester, default, arg1, arg2, arg3, arg4); + var f2 = TaskActionFactory.Create(catcher, default, arg1, arg2, arg3, arg4); + return SafeInvokeAsyncCore(f1, initializer, f2, ct); + } + + /// + /// Provides a generic way to abide the rule description of CA2000 (Dispose objects before losing scope). + /// + /// The type of the first parameter of the function delegate and delegate . + /// The type of the second parameter of the function delegate and delegate . + /// The type of the third parameter of the function delegate and delegate . + /// The type of the fourth parameter of the function delegate and delegate . + /// The type of the fifth parameter of the function delegate and delegate . + /// The type of the return value of the function delegate . + /// The function delegate that initializes an object implementing the interface. + /// The function delegate that is used to ensure that operations performed on abides CA2000. + /// The first parameter of the function delegate and delegate . + /// The second parameter of the function delegate and delegate . + /// The third parameter of the function delegate and delegate . + /// The fourth parameter of the function delegate and delegate . + /// The fifth parameter of the function delegate and delegate . + /// The token to monitor for cancellation requests. The default value is . + /// The function delegate that will handle any exceptions might thrown by . + /// A task that represents the asynchronous operation. The task result contains the return value of the function delegate if the operations succeeded; otherwise null if the operation failed. + public static Task SafeInvokeAsync(Func initializer, Func> tester, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, CancellationToken ct = default, Func catcher = null) where TResult : class, IDisposable + { + Validator.ThrowIfNull(initializer, nameof(initializer)); + Validator.ThrowIfNull(tester, nameof(tester)); + var f1 = TaskFuncFactory.Create(tester, default, arg1, arg2, arg3, arg4, arg5); + var f2 = TaskActionFactory.Create(catcher, default, arg1, arg2, arg3, arg4, arg5); + return SafeInvokeAsyncCore(f1, initializer, f2, ct); + } + + private static TResult SafeInvokeCore(FuncFactory testerFactory, Func initializer, ActionFactory catcherFactory) + where TResult : class, IDisposable + where TTester : Template + where TCatcher : Template + { + TResult result = null; + try + { + testerFactory.GenericArguments.Arg1 = initializer(); + testerFactory.GenericArguments.Arg1 = testerFactory.ExecuteMethod(); + result = testerFactory.GenericArguments.Arg1; + testerFactory.GenericArguments.Arg1 = null; + } + catch (Exception e) + { + if (!catcherFactory.HasDelegate) + { + throw; + } + else + { + catcherFactory.GenericArguments.Arg1 = e; + catcherFactory.ExecuteMethod(); + } + } + finally + { + testerFactory.GenericArguments.Arg1?.Dispose(); + } + return result; + } + + private static async Task SafeInvokeAsyncCore(TaskFuncFactory testerFactory, Func initializer, TaskActionFactory catcherFactory, CancellationToken ct) + where TResult : class, IDisposable + where TTester : Template + where TCatcher : Template + { + TResult result = null; + try + { + testerFactory.GenericArguments.Arg1 = initializer(); + testerFactory.GenericArguments.Arg1 = await testerFactory.ExecuteMethodAsync(ct).ConfigureAwait(false); + result = testerFactory.GenericArguments.Arg1; + testerFactory.GenericArguments.Arg1 = null; + } + catch (Exception e) + { + if (!catcherFactory.HasDelegate) + { + throw; + } + else + { + catcherFactory.GenericArguments.Arg1 = e; + await catcherFactory.ExecuteMethodAsync(ct).ConfigureAwait(false); + } + } + finally + { + testerFactory.GenericArguments.Arg1?.Dispose(); + } + return result; + } } } \ No newline at end of file diff --git a/src/Cuemon.Core/Security/Hash.cs b/src/Cuemon.Core/Security/Hash.cs index f63624b0a..0a498c545 100644 --- a/src/Cuemon.Core/Security/Hash.cs +++ b/src/Cuemon.Core/Security/Hash.cs @@ -264,7 +264,7 @@ public virtual HashResult ComputeHash(IEnumerable input) /// A containing the computed hash code of the specified . public virtual HashResult ComputeHash(Stream input) { - return ComputeHash(Disposable.SafeInvoke(() => new MemoryStream(), destination => + return ComputeHash(Patterns.SafeInvoke(() => new MemoryStream(), destination => { Decorator.Enclose(input).CopyStreamCore(destination); return destination; diff --git a/src/Cuemon.Core/Text/ByteOrderMark.cs b/src/Cuemon.Core/Text/ByteOrderMark.cs index 67735fade..ee5078af3 100644 --- a/src/Cuemon.Core/Text/ByteOrderMark.cs +++ b/src/Cuemon.Core/Text/ByteOrderMark.cs @@ -158,7 +158,7 @@ public static Stream Remove(Stream value, Encoding encoding, Action new MemoryStream(bytes.Length), ms => + return Patterns.SafeInvoke(() => new MemoryStream(bytes.Length), ms => { ms.Write(bytes, 0, bytes.Length); ms.Position = 0; diff --git a/src/Cuemon.Extensions.Xml/StreamExtensions.cs b/src/Cuemon.Extensions.Xml/StreamExtensions.cs index 34a711189..99393e9e2 100644 --- a/src/Cuemon.Extensions.Xml/StreamExtensions.cs +++ b/src/Cuemon.Extensions.Xml/StreamExtensions.cs @@ -51,7 +51,7 @@ public static Stream CopyXmlStream(this Stream value, Action } var options = Patterns.Configure(setup); - return Disposable.SafeInvoke(() => new MemoryStream(), ms => + return Patterns.SafeInvoke(() => new MemoryStream(), ms => { var document = new XmlDocument(); document.Load(value); @@ -94,7 +94,7 @@ public static Stream RemoveXmlNamespaceDeclarations(this Stream value, Action new MemoryStream(), ms => + return Patterns.SafeInvoke(() => new MemoryStream(), ms => { using (var writer = XmlWriter.Create(ms, options)) { diff --git a/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs b/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs index f795e4ae5..265a526be 100644 --- a/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs +++ b/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs @@ -456,7 +456,7 @@ public static Task DecompressDeflateAsync(this IDecorator decora private static Stream Compress(IDecorator decorator, StreamCompressionOptions options, Func decompressor) where T : Stream { - return Disposable.SafeInvoke(() => new MemoryStream(), target => + return Patterns.SafeInvoke(() => new MemoryStream(), target => { using (var compressed = decompressor(target, options.Level, true)) { @@ -471,7 +471,7 @@ private static Stream Compress(IDecorator decorator, StreamCompressio private static Task CompressAsync(IDecorator decorator, AsyncStreamCompressionOptions options, Func decompressor) where T : Stream { - return Disposable.SafeInvokeAsync(() => new MemoryStream(), async (target, ct) => + return Patterns.SafeInvokeAsync(() => new MemoryStream(), async (target, ct) => { #if NETSTANDARD2_1 await using (var compressed = decompressor(target, options.Level, true)) @@ -492,7 +492,7 @@ private static Task CompressAsync(IDecorator decorator, Async private static Stream Decompress(IDecorator decorator, StreamCopyOptions options, Func compressor) where T : Stream { - return Disposable.SafeInvoke(() => new MemoryStream(), target => + return Patterns.SafeInvoke(() => new MemoryStream(), target => { using (var uncompressed = compressor(decorator.Inner, CompressionMode.Decompress, true)) { @@ -507,7 +507,7 @@ private static Stream Decompress(IDecorator decorator, StreamCopyOpti private static Task DecompressAsync(IDecorator decorator, AsyncStreamCopyOptions options, Func compressor) where T : Stream { - return Disposable.SafeInvokeAsync(() => new MemoryStream(), async (target, ct) => + return Patterns.SafeInvokeAsync(() => new MemoryStream(), async (target, ct) => { #if NETSTANDARD2_1 await using (var uncompressed = compressor(decorator.Inner, CompressionMode.Decompress, true)) diff --git a/src/Cuemon.IO/StreamFactory.cs b/src/Cuemon.IO/StreamFactory.cs index ad588c4eb..37ccfd12d 100644 --- a/src/Cuemon.IO/StreamFactory.cs +++ b/src/Cuemon.IO/StreamFactory.cs @@ -115,7 +115,7 @@ public static Stream Create(Action(ActionFactory factory, Action setup = null) where TTuple : Template { var options = Patterns.Configure(setup); - return Disposable.SafeInvoke(() => new MemoryStream(options.BufferSize), (ms, f) => + return Patterns.SafeInvoke(() => new MemoryStream(options.BufferSize), (ms, f) => { var writer = new InternalStreamWriter(ms, options); { diff --git a/src/Cuemon.Net/NetDependency.cs b/src/Cuemon.Net/NetDependency.cs index 6c2b9d7aa..2dc54cd2c 100644 --- a/src/Cuemon.Net/NetDependency.cs +++ b/src/Cuemon.Net/NetDependency.cs @@ -341,7 +341,7 @@ public override void Start() case UriScheme.File: case UriScheme.Http: case UriScheme.Https: - var watcher = Disposable.SafeInvoke(() => new NetWatcher(uri, DueTime, Period, CheckResponseData), nw => + var watcher = Patterns.SafeInvoke(() => new NetWatcher(uri, DueTime, Period, CheckResponseData), nw => { nw.Changed += WatcherChanged; return nw; diff --git a/src/Cuemon.Security.Cryptography/AesCryptor.cs b/src/Cuemon.Security.Cryptography/AesCryptor.cs index b2baf8b17..c1f333330 100644 --- a/src/Cuemon.Security.Cryptography/AesCryptor.cs +++ b/src/Cuemon.Security.Cryptography/AesCryptor.cs @@ -93,7 +93,7 @@ private byte[] CryptoTransformCore(byte[] value, AesMode mode, Action new MemoryStream(), (ms, rijndael, bytes) => + using (var sms = Patterns.SafeInvoke(() => new MemoryStream(), (ms, rijndael, bytes) => { CryptoStream cryptoStream; switch (mode) diff --git a/src/Cuemon.Xml/XmlStreamFactory.cs b/src/Cuemon.Xml/XmlStreamFactory.cs index bff5f0822..dc7e599d6 100644 --- a/src/Cuemon.Xml/XmlStreamFactory.cs +++ b/src/Cuemon.Xml/XmlStreamFactory.cs @@ -18,7 +18,7 @@ public static class XmlStreamFactory public static Stream CreateStream(Action writer, Action setup = null) { var options = Patterns.Configure(setup); - return Disposable.SafeInvoke(() => new MemoryStream(), ms => + return Patterns.SafeInvoke(() => new MemoryStream(), ms => { using (var w = XmlWriter.Create(ms, options)) { diff --git a/test/Cuemon.Core.Tests/DisposableTest.cs b/test/Cuemon.Core.Tests/DisposableTest.cs index 45145b632..b060fd2de 100644 --- a/test/Cuemon.Core.Tests/DisposableTest.cs +++ b/test/Cuemon.Core.Tests/DisposableTest.cs @@ -22,7 +22,7 @@ public void SafeInvoke_ShouldAbideRuleCA2000() { var guid = Guid.NewGuid(); var called = 0; - var stream = Disposable.SafeInvoke(() => new MemoryStream(), ms => + var stream = Patterns.SafeInvoke(() => new MemoryStream(), ms => { called++; ms.WriteByte(1); @@ -35,7 +35,7 @@ public void SafeInvoke_ShouldAbideRuleCA2000() MemoryStream msRef = null; called = 0; - stream = Disposable.SafeInvoke(() => new MemoryStream(), (ms, g) => + stream = Patterns.SafeInvoke(() => new MemoryStream(), (ms, g) => { msRef = ms; Assert.Equal(guid, g); @@ -49,7 +49,7 @@ public void SafeInvoke_ShouldAbideRuleCA2000() Assert.Null(stream); Assert.Throws(() => msRef.Length); - stream = Disposable.SafeInvoke(() => new MemoryStream(), (ms, n1, n2, n3, n4, n5) => + stream = Patterns.SafeInvoke(() => new MemoryStream(), (ms, n1, n2, n3, n4, n5) => { called++; ms.Write(Decorator.Enclose($"{n1}{n2}{n3}{n4}{n5}").ToByteArray()); @@ -67,7 +67,7 @@ public async Task SafeInvokeAsync_ShouldAbideRuleCA2000() { var guid = Guid.NewGuid(); var called = 0; - var stream = await Disposable.SafeInvokeAsync(() => new MemoryStream(), async (ms, ct) => + var stream = await Patterns.SafeInvokeAsync(() => new MemoryStream(), async (ms, ct) => { called++; await ms.WriteAsync(new byte[] { 1 }, ct); @@ -80,7 +80,7 @@ public async Task SafeInvokeAsync_ShouldAbideRuleCA2000() MemoryStream msRef = null; called = 0; - stream = await Disposable.SafeInvokeAsync(() => new MemoryStream(), (ms, g, ct) => + stream = await Patterns.SafeInvokeAsync(() => new MemoryStream(), (ms, g, ct) => { msRef = ms; Assert.Equal(guid, g); @@ -100,7 +100,7 @@ await Assert.ThrowsAsync(async () => var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(5)); msRef = null; called = 0; - stream = await Disposable.SafeInvokeAsync(() => new MemoryStream(), async (ms, g, ct) => + stream = await Patterns.SafeInvokeAsync(() => new MemoryStream(), async (ms, g, ct) => { msRef = ms; Assert.Equal(guid, g); @@ -119,7 +119,7 @@ await Assert.ThrowsAsync(async () => Assert.Throws(() => msRef.Length); }); - stream = await Disposable.SafeInvokeAsync(() => new MemoryStream(), async (ms, n1, n2, n3, n4, n5, ct) => + stream = await Patterns.SafeInvokeAsync(() => new MemoryStream(), async (ms, n1, n2, n3, n4, n5, ct) => { called++; var bytes = Decorator.Enclose($"{n1}{n2}{n3}{n4}{n5}").ToByteArray(); From 0a47cf579e7549182f83baff73973cb86dbc9c30 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 22 Sep 2020 22:27:58 +0200 Subject: [PATCH 208/385] Increased timeout with 1 second. Again, ADO surprise. --- .../CacheEnumerableExtensionsTest.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs b/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs index c86525742..6e2ba9477 100644 --- a/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs +++ b/test/Cuemon.Extensions.Runtime.Caching.Tests/CacheEnumerableExtensionsTest.cs @@ -42,7 +42,7 @@ public void GetOrAdd_ShouldCacheAndReturnItemInOneGoUsingSlidingExpirationOfTenS Assert.Equal(items, _cache.Count()); Assert.True(bag.OrderBy(l => l).SequenceEqual(_cache.Where(pair => pair.Value.Namespace == CacheEntry.NoScope).Select(pair => pair.Key).OrderBy(l => l))); // insure thread safety validation - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count()); } @@ -88,7 +88,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingSlidingExpirationOf Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -138,7 +138,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingS Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -188,7 +188,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingS Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -238,7 +238,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsin Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -288,7 +288,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsing Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -338,7 +338,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsing Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -384,7 +384,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingAbsoluteExpirationO Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -434,7 +434,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingA Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -484,7 +484,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingA Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -534,7 +534,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsin Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -584,7 +584,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsing Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -634,7 +634,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsing Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -680,7 +680,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateUsingDependencyExpiratio Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -730,7 +730,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingOneParameterUsingD Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -780,7 +780,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingTwoParameterUsingD Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -830,7 +830,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingThreeParameterUsin Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -880,7 +880,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFourParameterUsing Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } @@ -930,7 +930,7 @@ public void Memoize_ShouldCacheAndReturnFunctionDelegateHavingFiveParameterUsing Assert.Equal(1, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); - Thread.Sleep(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(11)); Assert.Equal(0, _cache.Count(CacheEnumerableExtensions.MemoizationScope)); } From 0b59edb09f497e4ce62a4971ebda68f819f9db2e Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 23 Sep 2020 19:01:26 +0200 Subject: [PATCH 209/385] Updated package description, release notes and tags as well as minor xml-doc alignments. --- docfx/api/namespaces/Cuemon.Resilience.md | 4 +++- src/Cuemon.Core/Properties/PackageReleaseNotes.txt | 7 ++++++- src/Cuemon.Resilience/Cuemon.Resilience.csproj | 4 ++-- .../Properties/PackageReleaseNotes.txt | 10 ++++++++++ src/Cuemon.Resilience/TransientOperationOptions.cs | 3 +-- 5 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 src/Cuemon.Resilience/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Resilience.md b/docfx/api/namespaces/Cuemon.Resilience.md index 6cbae165d..c6e11f47f 100644 --- a/docfx/api/namespaces/Cuemon.Resilience.md +++ b/docfx/api/namespaces/Cuemon.Resilience.md @@ -2,4 +2,6 @@ uid: Cuemon.Resilience summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Resilience namespace contains types related to applying transient fault handling to existing code using intuitively named static methods that uses delegates to provide a lightweight resilience framework. + +Availability: NET Standard 2.0 \ No newline at end of file diff --git a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt index a53fe0ab2..5d02c174e 100644 --- a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt @@ -2,7 +2,7 @@ Availability: NET Standard 2.0   # Upgrade Steps -- [ACTION REQUIRED] +- To use the earlier built-in support for transient fault handling, please refer to the Cuemon.Resilience namespace, as it has been merged and refactored into this assembly -   # Breaking Changes @@ -19,6 +19,11 @@ Availability: NET Standard 2.0 - REMOVED SecurityToken class from the Cuemon.Security namespace - REMOVED SecurityTokenSettings class from the Cuemon.Security namespace (replaced with SignedUriOptions in the Cuemon.Extensions.Net.Security namespace) - REMOVED SecurityUtility class from the Cuemon.Security namespace +- LatencyException TransientOperation class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- MOVED TransientFaultEvidence class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- MOVED TransientFaultException class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- MOVED TransientOperation class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- MOVED TransientOperationOptions class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience)   # New Features - diff --git a/src/Cuemon.Resilience/Cuemon.Resilience.csproj b/src/Cuemon.Resilience/Cuemon.Resilience.csproj index 790ab057c..95798dae1 100644 --- a/src/Cuemon.Resilience/Cuemon.Resilience.csproj +++ b/src/Cuemon.Resilience/Cuemon.Resilience.csproj @@ -8,8 +8,8 @@ Cuemon.Resilience Cuemon.Resilience - The Cuemon.Resilience namespace contains a lightweight resilience framework that support transient fault handling of operations. - transient-fault-evidence transient-fault-exception transient-operation async-transient-operation latency-exception + The Cuemon.Resilience namespace contains types related to applying transient fault handling to existing code using intuitively named methods taking both Action{..} and Func{..} delegates to provide a lightweight resilience framework. + transient-fault-handling transient-fault-evidence transient-fault-exception transient-operation async-transient-operation latency-exception diff --git a/src/Cuemon.Resilience/Properties/PackageReleaseNotes.txt b/src/Cuemon.Resilience/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..0456ef2fc --- /dev/null +++ b/src/Cuemon.Resilience/Properties/PackageReleaseNotes.txt @@ -0,0 +1,10 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED LatencyException class in the Cuemon.Resilience namespace that represents the exception that is thrown when a latency related operation was taking to long to complete +- ADDED TransientFaultEvidence class in the Cuemon.Resilience namespace that provides evidence about a faulted TransientOperation +- ADDED TransientFaultException class in the Cuemon.Resilience namespace that represents the exception that is thrown when a transient fault handling was unsuccessful +- ADDED TransientOperation class in the Cuemon.Resilience namespace that provides a set of static methods that enable developers to make their applications more resilient by adding robust transient fault handling logic ideal for temporary condition such as network connectivity issues or service unavailability +- ADDED TransientOperationOptions class in the Cuemon.Resilience namespace that specifies options related to TransientOperation +  \ No newline at end of file diff --git a/src/Cuemon.Resilience/TransientOperationOptions.cs b/src/Cuemon.Resilience/TransientOperationOptions.cs index 661b2a9ed..164a7994d 100644 --- a/src/Cuemon.Resilience/TransientOperationOptions.cs +++ b/src/Cuemon.Resilience/TransientOperationOptions.cs @@ -3,9 +3,8 @@ namespace Cuemon.Resilience { /// - /// Specifies options that is related to handling of . + /// Configuration options for . /// - /// . public class TransientOperationOptions { /// From 7f7bec5d1295605046e84775825a9935b52b1b73 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 23 Sep 2020 22:44:17 +0200 Subject: [PATCH 210/385] Fixed private set to public get. --- src/Cuemon.Net/Http/HttpManagerOptions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Net/Http/HttpManagerOptions.cs b/src/Cuemon.Net/Http/HttpManagerOptions.cs index 7cc702548..6d16fd6fa 100644 --- a/src/Cuemon.Net/Http/HttpManagerOptions.cs +++ b/src/Cuemon.Net/Http/HttpManagerOptions.cs @@ -66,10 +66,10 @@ public HttpManagerOptions() public Dictionary DefaultRequestHeaders { get; } /// - /// Gets the HTTP handler stack to use for sending requests. + /// Gets or sets the HTTP handler stack to use for sending requests. /// /// The HTTP handler stack to use for sending requests. - public Func HandlerFactory { get; private set; } + public Func HandlerFactory { get; set; } /// /// Gets or sets the timespan to wait before the request times out. Default is 2 minutes. From 3279cd08a2f5904657cda3c963df4b1717f754d7 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 23 Sep 2020 23:09:53 +0200 Subject: [PATCH 211/385] Updated package description and release notes. Updated DocFx ns spec. --- docfx/api/namespaces/Cuemon.Net.Http.md | 6 +++++- docfx/api/namespaces/Cuemon.Net.Mail.md | 6 +++++- docfx/api/namespaces/Cuemon.Net.md | 6 +++++- src/Cuemon.Net/Cuemon.Net.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 20 +++++++++++++++++++ 5 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 src/Cuemon.Net/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Net.Http.md b/docfx/api/namespaces/Cuemon.Net.Http.md index c695f213d..8198a4d0c 100644 --- a/docfx/api/namespaces/Cuemon.Net.Http.md +++ b/docfx/api/namespaces/Cuemon.Net.Http.md @@ -2,4 +2,8 @@ uid: Cuemon.Net.Http summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Net.Http namespace contains types that is compliant with RFC 7231, section 4: Request methods and RFC 5789, section 2: Patch method while allowing custom definitions as well. The namespace is an addition to the System.Net.Http namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Net.Http namespace](https://docs.microsoft.com/en-us/dotnet/api/system.net.http?view=netstandard-2.0) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Net.Mail.md b/docfx/api/namespaces/Cuemon.Net.Mail.md index 8b19a206e..003a978c1 100644 --- a/docfx/api/namespaces/Cuemon.Net.Mail.md +++ b/docfx/api/namespaces/Cuemon.Net.Mail.md @@ -2,4 +2,8 @@ uid: Cuemon.Net.Mail summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Net.Mail namespace contains types that makes delivery of mail a piece of cake. The namespace is an addition to the System.Net.Mail namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Net.Mail namespace](https://docs.microsoft.com/en-us/dotnet/api/system.net.mail?view=netstandard-2.0) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Net.md b/docfx/api/namespaces/Cuemon.Net.md index d032278b7..7d53e18d2 100644 --- a/docfx/api/namespaces/Cuemon.Net.md +++ b/docfx/api/namespaces/Cuemon.Net.md @@ -2,4 +2,8 @@ uid: Cuemon.Net summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Net namespace contains types that provides a simple programming interface for HTTP and SMTP protocols. The namespace is an addition to the System.Net namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Net namespace](https://docs.microsoft.com/en-us/dotnet/api/system.net?view=netstandard-2.0) \ No newline at end of file diff --git a/src/Cuemon.Net/Cuemon.Net.csproj b/src/Cuemon.Net/Cuemon.Net.csproj index 6c0666f7f..7087d3c71 100644 --- a/src/Cuemon.Net/Cuemon.Net.csproj +++ b/src/Cuemon.Net/Cuemon.Net.csproj @@ -8,7 +8,7 @@ Cuemon.Net Cuemon.Net - The Cuemon.Net namespace contains classes for HTTP communication, a lightweight SMTP Client while and other neat features related to the System.Net namespace. + The Cuemon.Net namespace contains types that provides a simple programming interface for HTTP and SMTP protocols. The namespace is an addition to the System.Net namespace. http-manager http-get http-post http-put http-patch http-delete http-trace mail-distributor smtp-client diff --git a/src/Cuemon.Net/Properties/PackageReleaseNotes.txt b/src/Cuemon.Net/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..cd8a1977d --- /dev/null +++ b/src/Cuemon.Net/Properties/PackageReleaseNotes.txt @@ -0,0 +1,20 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# Upgrade Steps +- The Cuemon.Net.Mail assembly was removed with this version +- Any types found in the former Cuemon.Net.Mail namespace was merged into this assembly with and equivalent namespace +- Any former extension methods of the Cuemon.Net namespace was merged into the Cuemon.Extensions.Net namespace +  +# New Features +- ADDED MailDistributor class in the Cuemon.Net.Mail namespace that provides a way for applications to distribute one or more e-mails in batches by using the Simple Mail Transfer Protocol (SMTP) +- ADDED FieldValueSeparator enum in the Cuemon.Net namespace that specifies a range of key-value separators +- ADDED QueryStringCollection class in the Cuemon.Net namespace that provides a collection of string values that is equivalent to a query string of an Uri +  +# Breaking Changes +- REMOVED SetHandlerFactory{T} method on the HttpManagerOptions class (opt-in to allow set directly on HandlerFactory property) +  +# Improvements +- ADDED HttpManager constructor overload that takes a client factory delegate which creates and configures an HttpClient instance +- CHANGED HttpManagerOptions default value for DisposeHandler from true to false. This is due to the way Microsoft has designed the HttpClient with an implementation of IDisposable that could result in SocketException errors if not instantiated once and re-used throughout the life of an application, This setting reduces the risk of SocketException errors on existing code +  \ No newline at end of file From d34ba4b8afa795194126b88b38a62b186f1ec3e3 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 23 Sep 2020 23:37:12 +0200 Subject: [PATCH 212/385] DocFx ns specs. --- .../namespaces/Cuemon.Xml.Serialization.Converters.md | 4 +++- .../namespaces/Cuemon.Xml.Serialization.Formatters.md | 4 +++- docfx/api/namespaces/Cuemon.Xml.Serialization.md | 9 +++++++++ docfx/api/namespaces/Cuemon.Xml.XPath.md | 6 +++++- 4 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 docfx/api/namespaces/Cuemon.Xml.Serialization.md diff --git a/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md b/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md index 40c272c78..c3977fb9e 100644 --- a/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md +++ b/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md @@ -2,4 +2,6 @@ uid: Cuemon.Xml.Serialization.Converters summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Xml.Serialization.Converters namespace contains types tailored to resemble the [JsonConverter](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonConverter.htm) except we convert objects to and from XML. + +Availability: NET Standard 2.0 \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md b/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md index 74f4b9e11..bed1239dd 100644 --- a/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md +++ b/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md @@ -2,4 +2,6 @@ uid: Cuemon.Xml.Serialization.Formatters summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Xml.Serialization.Formatters namespace contains types that are used to serialize and deserialize objects into and from XML format using a generic signature. + +Availability: NET Standard 2.0 \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.Serialization.md b/docfx/api/namespaces/Cuemon.Xml.Serialization.md new file mode 100644 index 000000000..196f2d52b --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Xml.Serialization.md @@ -0,0 +1,9 @@ +--- +uid: Cuemon.Xml.Serialization +summary: *content +--- +The Cuemon.Xml.Serialization namespace contains types that are used to serialize and deserialize objects into and from XML format. The namespace is an addition to the System.Xml.Serialization namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Xml.Serialization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization?view=netstandard-2.0) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.XPath.md b/docfx/api/namespaces/Cuemon.Xml.XPath.md index 03f396c5f..0c8931e16 100644 --- a/docfx/api/namespaces/Cuemon.Xml.XPath.md +++ b/docfx/api/namespaces/Cuemon.Xml.XPath.md @@ -2,4 +2,8 @@ uid: Cuemon.Xml.XPath summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Xml.XPath namespace contains types related to easing creation of XPathDocument instances. The namespace is an addition to the System.Xml.XPath namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Xml.XPath namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.xpath?view=netstandard-2.0) \ No newline at end of file From 4d35dd1aee660405d56135e0b495b3e2c5ed8bd5 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 24 Sep 2020 00:19:50 +0200 Subject: [PATCH 213/385] Minor adjustments and cleanup. --- src/Cuemon.IO/AsyncStreamEncodingOptions.cs | 2 +- src/Cuemon.IO/AsyncStreamOptions.cs | 18 ----------------- src/Cuemon.IO/AsyncStreamReaderOptions.cs | 22 +++++++++++++++++---- src/Cuemon.IO/StreamEncodingOptions.cs | 2 +- src/Cuemon.IO/StreamOptions.cs | 18 ----------------- src/Cuemon.IO/StreamWriterOptions.cs | 4 ++-- 6 files changed, 22 insertions(+), 44 deletions(-) delete mode 100644 src/Cuemon.IO/AsyncStreamOptions.cs delete mode 100644 src/Cuemon.IO/StreamOptions.cs diff --git a/src/Cuemon.IO/AsyncStreamEncodingOptions.cs b/src/Cuemon.IO/AsyncStreamEncodingOptions.cs index 5c99b5a7f..164d00afd 100644 --- a/src/Cuemon.IO/AsyncStreamEncodingOptions.cs +++ b/src/Cuemon.IO/AsyncStreamEncodingOptions.cs @@ -7,7 +7,7 @@ namespace Cuemon.IO /// /// Configuration options for . /// - public class AsyncStreamEncodingOptions : AsyncStreamOptions, IEncodingOptions + public class AsyncStreamEncodingOptions : AsyncDisposableOptions, IEncodingOptions { /// /// Initializes a new instance of the class. diff --git a/src/Cuemon.IO/AsyncStreamOptions.cs b/src/Cuemon.IO/AsyncStreamOptions.cs deleted file mode 100644 index cc44c2f7f..000000000 --- a/src/Cuemon.IO/AsyncStreamOptions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.IO; - -namespace Cuemon.IO -{ - /// - /// Configuration options for . - /// - public class AsyncStreamOptions : AsyncDisposableOptions - { - - /// - /// Initializes a new instance of the class. - /// - public AsyncStreamOptions() - { - } - } -} \ No newline at end of file diff --git a/src/Cuemon.IO/AsyncStreamReaderOptions.cs b/src/Cuemon.IO/AsyncStreamReaderOptions.cs index 01e9260b8..493934f8f 100644 --- a/src/Cuemon.IO/AsyncStreamReaderOptions.cs +++ b/src/Cuemon.IO/AsyncStreamReaderOptions.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using Cuemon.Text; namespace Cuemon.IO @@ -8,6 +9,8 @@ namespace Cuemon.IO /// public class AsyncStreamReaderOptions : AsyncStreamEncodingOptions { + private int _bufferSize; + /// /// Initializes a new instance of the class. /// @@ -38,9 +41,20 @@ public AsyncStreamReaderOptions() } /// - /// Gets or sets the minimum size of the buffer. + /// Gets or sets the size of the buffer. /// - /// The minimum size of the buffer. - public int BufferSize { get; set; } + /// The size of the buffer. + /// + /// is lower than or equal to 0. + /// + public int BufferSize + { + get => _bufferSize; + set + { + Validator.ThrowIfLowerThanOrEqual(value, 0, nameof(value)); + _bufferSize = value; + } + } } } \ No newline at end of file diff --git a/src/Cuemon.IO/StreamEncodingOptions.cs b/src/Cuemon.IO/StreamEncodingOptions.cs index c8d1a02d0..1f0da32aa 100644 --- a/src/Cuemon.IO/StreamEncodingOptions.cs +++ b/src/Cuemon.IO/StreamEncodingOptions.cs @@ -7,7 +7,7 @@ namespace Cuemon.IO /// /// Configuration options for . /// - public class StreamEncodingOptions : StreamOptions, IEncodingOptions + public class StreamEncodingOptions : DisposableOptions, IEncodingOptions { /// /// Initializes a new instance of the class. diff --git a/src/Cuemon.IO/StreamOptions.cs b/src/Cuemon.IO/StreamOptions.cs deleted file mode 100644 index 633d2c1b4..000000000 --- a/src/Cuemon.IO/StreamOptions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.IO; - -namespace Cuemon.IO -{ - /// - /// Configuration options for . - /// - public class StreamOptions : DisposableOptions - { - - /// - /// Initializes a new instance of the class. - /// - public StreamOptions() - { - } - } -} \ No newline at end of file diff --git a/src/Cuemon.IO/StreamWriterOptions.cs b/src/Cuemon.IO/StreamWriterOptions.cs index 88a13ab41..13e11b183 100644 --- a/src/Cuemon.IO/StreamWriterOptions.cs +++ b/src/Cuemon.IO/StreamWriterOptions.cs @@ -6,9 +6,9 @@ namespace Cuemon.IO { /// - /// Specifies options that is related to operations. This class cannot be inherited. + /// Configuration options for . /// - public sealed class StreamWriterOptions : StreamEncodingOptions + public class StreamWriterOptions : StreamEncodingOptions { /// /// Initializes a new instance of the class. From 8d6d895c013401cf025b262c5c18e8373e19e646 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 24 Sep 2020 00:29:14 +0200 Subject: [PATCH 214/385] Added missing test for diff. algorithms. --- .../StreamDecoratorExtensionsTest.cs | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/test/Cuemon.IO.Tests/StreamDecoratorExtensionsTest.cs b/test/Cuemon.IO.Tests/StreamDecoratorExtensionsTest.cs index f2c677121..0f7fe835c 100644 --- a/test/Cuemon.IO.Tests/StreamDecoratorExtensionsTest.cs +++ b/test/Cuemon.IO.Tests/StreamDecoratorExtensionsTest.cs @@ -64,6 +64,39 @@ public async Task CompressBrotliAsync_ShouldCompressAndDecompress() TestOutput.WriteLine($"Decompressed ({ByteMultipleTable.FromBytes(dos.Length)}): {dosResult.Substring(0, 50)} ..."); } + [Fact] + public async Task CompressBrotliAsync_ShouldThrowTaskCanceledException() + { + var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(5)); + var size = 1024 * 1024; + var fs = Generate.RandomString(size); + var os = await Decorator.Enclose(fs).ToStreamAsync(); + await Assert.ThrowsAsync(async () => await Decorator.Enclose(os).CompressBrotliAsync(o => o.CancellationToken = ctsShouldFail.Token)); + } + + [Fact] + public void CompressGZip_ShouldCompressAndDecompress() + { + var size = 1024 * 1024; + var fs = Generate.RandomString(size); + var os = Decorator.Enclose(fs).ToStream(); + var cos = Decorator.Enclose(os).CompressGZip(); + var dos = Decorator.Enclose(cos).DecompressGZip(); + var osResult = Decorator.Enclose(os).ToEncodedString(o => o.LeaveOpen = true); + var cosResult = Decorator.Enclose(cos).ToEncodedString(o => o.LeaveOpen = true); + var dosResult = Decorator.Enclose(dos).ToEncodedString(o => o.LeaveOpen = true); + + Assert.Equal(size, os.Length); + Assert.NotEqual(os.Length, cos.Length); + Assert.True(os.Length > cos.Length); + Assert.Equal(os.Length, dos.Length); + Assert.Equal(osResult, dosResult); + Assert.NotEqual(osResult, cosResult); + + TestOutput.WriteLine($"Original ({ByteMultipleTable.FromBytes(os.Length)}): {osResult.Substring(0, 50)} ..."); + TestOutput.WriteLine($"Compressed ({ByteMultipleTable.FromBytes(cos.Length)}): {cosResult.Substring(0, 50)} ..."); + TestOutput.WriteLine($"Decompressed ({ByteMultipleTable.FromBytes(dos.Length)}): {dosResult.Substring(0, 50)} ..."); + } [Fact] public async Task CompressGZipAsync_ShouldCompressAndDecompress() @@ -124,16 +157,16 @@ public void CompressDeflate_ShouldCompressAndDecompress() } [Fact] - public void CompressGZip_ShouldCompressAndDecompress() + public async Task CompressDeflateAsync_ShouldCompressAndDecompress() { var size = 1024 * 1024; var fs = Generate.RandomString(size); - var os = Decorator.Enclose(fs).ToStream(); - var cos = Decorator.Enclose(os).CompressGZip(); - var dos = Decorator.Enclose(cos).DecompressGZip(); - var osResult = Decorator.Enclose(os).ToEncodedString(o => o.LeaveOpen = true); - var cosResult = Decorator.Enclose(cos).ToEncodedString(o => o.LeaveOpen = true); - var dosResult = Decorator.Enclose(dos).ToEncodedString(o => o.LeaveOpen = true); + var os = await Decorator.Enclose(fs).ToStreamAsync(); + var cos = await Decorator.Enclose(os).CompressDeflateAsync(); + var dos = await Decorator.Enclose(cos).DecompressDeflateAsync(); + var osResult = await Decorator.Enclose(os).ToEncodedStringAsync(o => o.LeaveOpen = true); + var cosResult = await Decorator.Enclose(cos).ToEncodedStringAsync(o => o.LeaveOpen = true); + var dosResult = await Decorator.Enclose(dos).ToEncodedStringAsync(o => o.LeaveOpen = true); Assert.Equal(size, os.Length); Assert.NotEqual(os.Length, cos.Length); @@ -147,6 +180,16 @@ public void CompressGZip_ShouldCompressAndDecompress() TestOutput.WriteLine($"Decompressed ({ByteMultipleTable.FromBytes(dos.Length)}): {dosResult.Substring(0, 50)} ..."); } + [Fact] + public async Task CompressDeflateAsync_ShouldThrowTaskCanceledException() + { + var ctsShouldFail = new CancellationTokenSource(TimeSpan.FromMilliseconds(5)); + var size = 1024 * 1024; + var fs = Generate.RandomString(size); + var os = await Decorator.Enclose(fs).ToStreamAsync(); + await Assert.ThrowsAsync(async () => await Decorator.Enclose(os).CompressDeflateAsync(o => o.CancellationToken = ctsShouldFail.Token)); + } + [Fact] public void ToByteArray_ShouldConvertStreamToByteArrayWithDefaultOptions() { From 805f4a5dec7da17715c9cafef07bef9dce1e2b95 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 24 Sep 2020 00:31:36 +0200 Subject: [PATCH 215/385] Updated package description, release notes and tags as well updated DocFx ns spec. --- docfx/api/namespaces/Cuemon.IO.md | 6 ++- .../Properties/PackageReleaseNotes.txt | 5 ++- src/Cuemon.IO/Cuemon.IO.csproj | 4 +- .../Properties/PackageReleaseNotes.txt | 31 ++++++++++++++++ .../Properties/PackageReleaseNotes.txt | 37 ++++++++++--------- 5 files changed, 60 insertions(+), 23 deletions(-) create mode 100644 src/Cuemon.IO/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.IO.md b/docfx/api/namespaces/Cuemon.IO.md index 3f19e0001..7a5ad3a07 100644 --- a/docfx/api/namespaces/Cuemon.IO.md +++ b/docfx/api/namespaces/Cuemon.IO.md @@ -2,4 +2,8 @@ uid: Cuemon.IO summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.IO namespace contains types primarily focusing on configuration options for IO related operations. The namespace is an addition to the System.IO namespace. + +Availability: NET Standard 2.0, NET Standard 2.1 + +Complements: [System.IO namespace](https://docs.microsoft.com/en-us/dotnet/api/system.io?view=netstandard-2.1) \ No newline at end of file diff --git a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt index 5d02c174e..4ba2906f2 100644 --- a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt @@ -1,4 +1,4 @@ -6.0.0 +Version: 6.0.0 Availability: NET Standard 2.0   # Upgrade Steps @@ -19,7 +19,8 @@ Availability: NET Standard 2.0 - REMOVED SecurityToken class from the Cuemon.Security namespace - REMOVED SecurityTokenSettings class from the Cuemon.Security namespace (replaced with SignedUriOptions in the Cuemon.Extensions.Net.Security namespace) - REMOVED SecurityUtility class from the Cuemon.Security namespace -- LatencyException TransientOperation class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- MOVED LatencyException class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- MOVED TransientOperation class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) - MOVED TransientFaultEvidence class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) - MOVED TransientFaultException class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) - MOVED TransientOperation class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) diff --git a/src/Cuemon.IO/Cuemon.IO.csproj b/src/Cuemon.IO/Cuemon.IO.csproj index e3b2d643b..deeea308d 100644 --- a/src/Cuemon.IO/Cuemon.IO.csproj +++ b/src/Cuemon.IO/Cuemon.IO.csproj @@ -8,8 +8,8 @@ Cuemon.IO Cuemon.IO - The Cuemon.IO namespace provides access to features that extends the System.IO namespace through IDecorator extension methods. - textreader textwriter brotli gzip deflate async + The Cuemon.IO namespace contains types primarily focusing on configuration options for IO related operations. The namespace is an addition to the System.IO namespace. + text-reader text-writer compress decompress compression decompression conversion encoding brotli gzip deflate async diff --git a/src/Cuemon.IO/Properties/PackageReleaseNotes.txt b/src/Cuemon.IO/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..b8e43a880 --- /dev/null +++ b/src/Cuemon.IO/Properties/PackageReleaseNotes.txt @@ -0,0 +1,31 @@ +Version: 6.0.0 +Availability: NET Standard 2.0, NET Standard 2.1 +  +# Upgrade Steps +- The Cuemon.IO.Compression namespace was removed with this version +- Any former extension methods of the Cuemon.IO namespace was merged into the Cuemon.Extensions.IO namespace +  +# Breaking Changes +- REMOVED CompressionType enum from the Cuemon.IO.Compression namespace +- REMOVED CompressionUtility class from the Cuemon.IO.Compression namespace +- REMOVED CompressionUtilityExtensions class from the Cuemon.IO.Compression namespace +- REMOVED FileInfoConverter class from the Cuemon.IO namespace +- REMOVED StreamConverter class from the Cuemon.IO namespace +- REMOVED StreamConverterExtensions class from the Cuemon.IO namespace +- REPLACED StreamWriterUtility class in the Cuemon.IO namespace with StreamFactory (and reduced overloads to max. 5 generic parameters) +- REMOVED TextReaderConverter class from Cuemon.IO namespace +- REMOVED TextReaderConverterExtensions class from the Cuemon.IO namespace +  +# New Features +- ADDED AsyncDisposableOptions class in the Cuemon.IO namespace that specifies options related to a cancelable IDisposable implementation +- ADDED AsyncStreamCompressionOptions class in the Cuemon.IO namespace that specifies options related to a cancelable Stream compression +- ADDED AsyncStreamCopyOptions class in the Cuemon.IO namespace that specifies options related to a cancelable Stream copy operation +- ADDED AsyncStreamEncodingOptions class in the Cuemon.IO namespace that specifies options related to a cancelable Stream encoding +- ADDED AsyncStreamReaderOptions class in the Cuemon.IO namespace that specifies options related to a cancelable StreamReader operation +- ADDED FileInfoOptions class in the Cuemon.IO namespace that specifies options related to FileInfo +- ADDED StreamCompressionOptions class in the Cuemon.IO namespace that specifies options related to a Stream compression +- ADDED StreamCopyOptions class in the Cuemon.IO namespace that specifies options related to a Stream copy operation +- ADDED StreamEncodingOptions class in the Cuemon.IO namespace that specifies options related to a Stream encoding +- ADDED StreamReaderOptions class in the Cuemon.IO namespace that specifies options related to a StreamReader operation +- ADDED StreamWriterOptions class in the Cuemon.IO namespace that specifies options related to a StreamWriter operation +  \ No newline at end of file diff --git a/src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt b/src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt index c48e7b916..246acbe17 100644 --- a/src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt @@ -7,23 +7,6 @@ Availability: NET Standard 2.0 - Any former extension methods of the Cuemon.Security namespace was removed completely due to the new intuitive static factory classes (HashFactory, KeyedHashFactory and UnkeyedHashFactory) - The extent of refactoring applied to this project resulted in so many breaking changes that a git diff is advisable   -# New Features -- ADDED AesCryptor class in the Cuemon.Security.Cryptography namespace that provides an implementation of the Advanced Encryption Standard (AES) symmetric algorithm -- ADDED AesCryptorOptions class in the Cuemon.Security.Cryptography namespace that specifies options related to AesCryptor -- ADDED AesKeyOptions class in the Cuemon.Security.Cryptography namespace that specifies options related to AesCryptor.GenerateKey -- ADDED HmacMessageDigest5 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the MD5 hash function -- ADDED HmacSecureHashAlgorithm1 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA1 hash function -- ADDED HmacSecureHashAlgorithm256 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA256 hash function -- ADDED HmacSecureHashAlgorithm384 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA384 hash function -- ADDED HmacSecureHashAlgorithm512 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA512 hash function -- ADDED KeyedCryptoHash class in the Cuemon.Security.Cryptography namespace that represents the base class from which all implementations of Hash-based Message Authentication Code (HMAC) should derive -- ADDED MessageDigest5 class in the Cuemon.Security.Cryptography namespace that provides a MD5 implementation of the MD (Message Digest) cryptographic hashing algorithm for 128-bit hash values -- ADDED SecureHashAlgorithm1 class in the Cuemon.Security.Cryptography namespace that provides a SHA-1 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 160-bit hash values -- ADDED SecureHashAlgorithm256 class in the Cuemon.Security.Cryptography namespace that provides a SHA-256 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 256-bit hash values -- ADDED SecureHashAlgorithm384 class in the Cuemon.Security.Cryptography namespace that provides a SHA-384 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 384-bit hash values -- ADDED SecureHashAlgorithm512 class in the Cuemon.Security.Cryptography namespace that provides a SHA-512 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 512-bit hash values -- ADDED UnkeyedCryptoHash class in the Cuemon.Security.Cryptography namespace that represents the base class from which all implementations of cryptographic hashing algorithm should derive -  # Breaking Changes - REPLACED AdvancedEncryptionStandardKeySize enum in the Cuemon.Security.Cryptography namespace with AesSize - REMOVED AdvancedEncryptionStandardUtility class from the Cuemon.Security.Cryptography namespace @@ -43,4 +26,22 @@ Availability: NET Standard 2.0 - REMOVED StreamKeyedHashOptions class from the Cuemon.Security.Cryptography namespace - REMOVED StringHashOptions class from the Cuemon.Security.Cryptography namespace - REMOVED StringKeyedHashOptions class from the Cuemon.Security.Cryptography namespace -- REMOVED StrongNumberUtility class from the Cuemon.Security.Cryptography namespace (replaced with Generate.RandomNumber in the Cuemon namespace) \ No newline at end of file +- REMOVED StrongNumberUtility class from the Cuemon.Security.Cryptography namespace (replaced with Generate.RandomNumber in the Cuemon namespace) +  +# New Features +- ADDED AesCryptor class in the Cuemon.Security.Cryptography namespace that provides an implementation of the Advanced Encryption Standard (AES) symmetric algorithm +- ADDED AesCryptorOptions class in the Cuemon.Security.Cryptography namespace that specifies options related to AesCryptor +- ADDED AesKeyOptions class in the Cuemon.Security.Cryptography namespace that specifies options related to AesCryptor.GenerateKey +- ADDED HmacMessageDigest5 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the MD5 hash function +- ADDED HmacSecureHashAlgorithm1 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA1 hash function +- ADDED HmacSecureHashAlgorithm256 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA256 hash function +- ADDED HmacSecureHashAlgorithm384 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA384 hash function +- ADDED HmacSecureHashAlgorithm512 class in the Cuemon.Security.Cryptography namespace that provides a Hash-based Message Authentication Code (HMAC) using the SHA512 hash function +- ADDED KeyedCryptoHash class in the Cuemon.Security.Cryptography namespace that represents the base class from which all implementations of Hash-based Message Authentication Code (HMAC) should derive +- ADDED MessageDigest5 class in the Cuemon.Security.Cryptography namespace that provides a MD5 implementation of the MD (Message Digest) cryptographic hashing algorithm for 128-bit hash values +- ADDED SecureHashAlgorithm1 class in the Cuemon.Security.Cryptography namespace that provides a SHA-1 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 160-bit hash values +- ADDED SecureHashAlgorithm256 class in the Cuemon.Security.Cryptography namespace that provides a SHA-256 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 256-bit hash values +- ADDED SecureHashAlgorithm384 class in the Cuemon.Security.Cryptography namespace that provides a SHA-384 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 384-bit hash values +- ADDED SecureHashAlgorithm512 class in the Cuemon.Security.Cryptography namespace that provides a SHA-512 implementation of the SHA (Secure Hash Algorithm) cryptographic hashing algorithm for 512-bit hash values +- ADDED UnkeyedCryptoHash class in the Cuemon.Security.Cryptography namespace that represents the base class from which all implementations of cryptographic hashing algorithm should derive +  \ No newline at end of file From a7eee3f9532a61ecbed690b9441972d8397a98f2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 24 Sep 2020 23:18:58 +0200 Subject: [PATCH 216/385] Updated DocFx namespace description. --- .gitignore | 3 ++- .../namespaces/Cuemon.Collections.Generic.md | 17 +++++++++++++++- docfx/api/namespaces/Cuemon.Collections.md | 15 +++++++++++++- docfx/api/namespaces/Cuemon.Configuration.md | 13 +++++++++++- .../Cuemon.Extensions.Xunit.Hosting.md | 13 +++++++++++- .../api/namespaces/Cuemon.Extensions.Xunit.md | 13 +++++++++++- docfx/api/namespaces/Cuemon.Globalization.md | 15 +++++++++++++- docfx/api/namespaces/Cuemon.Messaging.md | 15 +++++++++++++- docfx/api/namespaces/Cuemon.Reflection.md | 15 +++++++++++++- docfx/api/namespaces/Cuemon.Resilience.md | 13 ++++++++++-- .../api/namespaces/Cuemon.Runtime.Caching.md | 13 +++++++++++- ...Cuemon.Runtime.Serialization.Formatters.md | 15 +++++++++++++- .../Cuemon.Runtime.Serialization.md | 15 +++++++++++++- docfx/api/namespaces/Cuemon.Runtime.md | 15 +++++++++++++- .../Cuemon.Security.Cryptography.md | 11 +++++++++- docfx/api/namespaces/Cuemon.Security.md | 15 +++++++++++++- docfx/api/namespaces/Cuemon.Text.md | 15 +++++++++++++- docfx/api/namespaces/Cuemon.Threading.md | 13 +++++++++++- docfx/api/namespaces/Cuemon.Xml.Linq.md | 20 +++++++++++++++++++ .../Cuemon.Xml.Serialization.Converters.md | 13 +++++++++++- .../Cuemon.Xml.Serialization.Formatters.md | 11 +++++++++- .../namespaces/Cuemon.Xml.Serialization.md | 13 +++++++++++- docfx/api/namespaces/Cuemon.Xml.XPath.md | 11 +++++++++- docfx/api/namespaces/Cuemon.Xml.md | 13 +++++++++++- docfx/api/namespaces/Cuemon.md | 17 +++++++++++++++- docfx/templates/cuemon/index.html.tmpl | 2 ++ 26 files changed, 319 insertions(+), 25 deletions(-) create mode 100644 docfx/api/namespaces/Cuemon.Xml.Linq.md diff --git a/.gitignore b/.gitignore index 1ad672d44..7a45a17b2 100644 --- a/.gitignore +++ b/.gitignore @@ -224,4 +224,5 @@ ModelManifest.xml /docfx/wwwroot /docfx/api/**/*.yml /docfx/**/*.manifest -/.vscode/docfx-assistant +/docfx/.vscode/docfx-assistant +/.vscode/docfx-assistant \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Collections.Generic.md b/docfx/api/namespaces/Cuemon.Collections.Generic.md index 17963039f..5b56675e0 100644 --- a/docfx/api/namespaces/Cuemon.Collections.Generic.md +++ b/docfx/api/namespaces/Cuemon.Collections.Generic.md @@ -2,4 +2,19 @@ uid: Cuemon.Collections.Generic summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Collections.Generic namespace contains types that define generic collections that support paging, partitioning, dynamic comparers and some specialized collections such as a read-only enum dictionary and a generic, conditional collection. The namespace is an addition to the System.Collections.Generic namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Collections.Generic namespace](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic?view=netstandard-2.0) 🔗 + +Related: [Cuemon.Extensions.Collections.Generic namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Collections.Generic.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Collections/Generic)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core/Collections/Generic)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core/Collections/Generic) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core/) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Collections.md b/docfx/api/namespaces/Cuemon.Collections.md index 5f42234e2..de1a07ba5 100644 --- a/docfx/api/namespaces/Cuemon.Collections.md +++ b/docfx/api/namespaces/Cuemon.Collections.md @@ -2,4 +2,17 @@ uid: Cuemon.Collections summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Collections namespace contains types that define various collections of objects. The namespace is an addition to the System.Collections namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Collections namespace](https://docs.microsoft.com/en-us/dotnet/api/system.collections?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Collections)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core/Collections)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core/Collections) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core/) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Configuration.md b/docfx/api/namespaces/Cuemon.Configuration.md index 2856100c8..7b9ab43a0 100644 --- a/docfx/api/namespaces/Cuemon.Configuration.md +++ b/docfx/api/namespaces/Cuemon.Configuration.md @@ -2,4 +2,15 @@ uid: Cuemon.Configuration summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Configuration namespace contains types focusing on writing configurable classes to help suport adhering to Separation of Concerns (SoC). + +Availability: NET Standard 2.0 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Configuration)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core/Configuration)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core/Configuration) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core/) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md index c7c7aee70..11ab0bfb6 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md @@ -4,4 +4,15 @@ summary: *content --- The Cuemon.Extensions.Xunit.Hosting namespace contains types that provides a uniform way of doing unit testing that is used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. -Availability: NET Standard 2.0, NET Core 3.0 \ No newline at end of file +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [xUnit: Shared Context between Tests](https://xunit.net/docs/shared-context) 🔗 + +Github branches: 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xunit.Hosting)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Xunit.Hosting)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Xunit.Hosting) + +NuGet packages 📦\ +[Cuemon.Extensions.Xunit.Hosting (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Xunit.Hosting)\ +[Cuemon.Extensions.Xunit.Hosting (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Xunit.Hosting) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.md index 2bd1476e5..da0928724 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xunit.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.md @@ -4,4 +4,15 @@ summary: *content --- The Cuemon.Extensions.Xunit namespace contains types that provides a uniform way of doing unit testing. The namespace relates to the Xunit.Abstractions namespace. -Availability: NET Standard 2.0 \ No newline at end of file +Availability: NET Standard 2.0 + +Complements: [xUnit: Capturing Output](https://xunit.net/docs/capturing-output) 🔗 + +Github branches: 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xunit)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Xunit)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Xunit) + +NuGet packages 📦\ +[Cuemon.Extensions.Xunit (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Xunit)\ +[Cuemon.Extensions.Xunit (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Xunit) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Globalization.md b/docfx/api/namespaces/Cuemon.Globalization.md index 58e69201d..9a0d9460c 100644 --- a/docfx/api/namespaces/Cuemon.Globalization.md +++ b/docfx/api/namespaces/Cuemon.Globalization.md @@ -2,4 +2,17 @@ uid: Cuemon.Globalization summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Globalization namespace contains types that focuses on culture-related information, including language, country/region and localized resources useful for writing globalized (internationalized) applications. The namespace is an addition to the System.Globalization namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Globalization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.globalization?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Globalization)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core/Globalization)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core/Globalization) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Messaging.md b/docfx/api/namespaces/Cuemon.Messaging.md index 5c896f629..1fcd0c413 100644 --- a/docfx/api/namespaces/Cuemon.Messaging.md +++ b/docfx/api/namespaces/Cuemon.Messaging.md @@ -2,4 +2,17 @@ uid: Cuemon.Messaging summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Messaging namespace contains types that assist in more advanced scenarios such as CQRS, microservices and event-driven architecture. The namespace is an addition to the System.Messaging namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Messaging namespace](https://docs.microsoft.com/en-us/dotnet/api/system.messaging?view=netframework-4.6.1) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Messaging)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core/Messaging)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core/Messaging) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Reflection.md b/docfx/api/namespaces/Cuemon.Reflection.md index d5ca2c9d0..099998e6a 100644 --- a/docfx/api/namespaces/Cuemon.Reflection.md +++ b/docfx/api/namespaces/Cuemon.Reflection.md @@ -2,4 +2,17 @@ uid: Cuemon.Reflection summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Reflection namespace contains types that focuses on working natural with the hidden gems of reflection in order to retrieve information about assemblies, members, parameters, and different versioning schemes that support both traditional and semantic. The namespace is an addition to the System.Reflection namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Reflection namespace](https://docs.microsoft.com/en-us/dotnet/api/system.reflection?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Reflection)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core/Reflection)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core/Reflection) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Resilience.md b/docfx/api/namespaces/Cuemon.Resilience.md index c6e11f47f..1def8a5ef 100644 --- a/docfx/api/namespaces/Cuemon.Resilience.md +++ b/docfx/api/namespaces/Cuemon.Resilience.md @@ -2,6 +2,15 @@ uid: Cuemon.Resilience summary: *content --- -The Cuemon.Resilience namespace contains types related to applying transient fault handling to existing code using intuitively named static methods that uses delegates to provide a lightweight resilience framework. +The Cuemon.Resilience namespace contains types related to applying transient fault handling to existing code using intuitively named methods taking both Action{..} and Func{..} delegates to provide a lightweight resilience framework. -Availability: NET Standard 2.0 \ No newline at end of file +Availability: NET Standard 2.0 + +Github branches: 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Resilience)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Resilience)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Resilience) + +NuGet packages 📦\ +[Cuemon.Resilience (CI)](https://nuget.cuemon.net/packages/Cuemon.Resilience)\ +[Cuemon.Resilience (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Resilience) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Runtime.Caching.md b/docfx/api/namespaces/Cuemon.Runtime.Caching.md index 163e5360d..a6afe57be 100644 --- a/docfx/api/namespaces/Cuemon.Runtime.Caching.md +++ b/docfx/api/namespaces/Cuemon.Runtime.Caching.md @@ -6,4 +6,15 @@ The Cuemon.Runtime.Caching namespace contains types related to interfaces for ge Availability: NET Standard 2.0 -Complements: [System.Runtime.Caching namespace](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.caching?view=netframework-4.6.1) \ No newline at end of file +Complements: [System.Runtime.Caching namespace](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.caching?view=netframework-4.6.1) 🔗 + +Related: [Cuemon.Extensions.Runtime.Caching namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Runtime.Caching.html) 📘 + +Github branches: 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Runtime.Caching)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Runtime.Caching)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Runtime.Caching) + +NuGet packages 📦\ +[Cuemon.Runtime.Caching (CI)](https://nuget.cuemon.net/packages/Cuemon.Runtime.Caching)\ +[Cuemon.Runtime.Caching (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Runtime.Caching) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md b/docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md index 989fdf49e..801aa37e2 100644 --- a/docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md +++ b/docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md @@ -2,4 +2,17 @@ uid: Cuemon.Runtime.Serialization.Formatters summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Runtime.Serialization.Formatters namespace contains types that are used to serialize and deserialize objects into and from a generic type. The namespace is an addition to the System.Runtime.Serialization namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Runtime.Serialization.Formatters namespace](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Runtime/Serialization/Formatters)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core/Runtime/Serialization/Formatters)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core/Runtime/Serialization/Formatters) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Runtime.Serialization.md b/docfx/api/namespaces/Cuemon.Runtime.Serialization.md index 9f8aac99a..312cbc133 100644 --- a/docfx/api/namespaces/Cuemon.Runtime.Serialization.md +++ b/docfx/api/namespaces/Cuemon.Runtime.Serialization.md @@ -2,4 +2,17 @@ uid: Cuemon.Runtime.Serialization summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Runtime.Serialization namespace contains types that are used to serialize objects into a hierarchy of nodes. The namespace is an addition to the System.Runtime.Serialization namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Runtime.Serialization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Runtime/Serialization)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core/Runtime/Serialization)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core/Runtime/Serialization) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Runtime.md b/docfx/api/namespaces/Cuemon.Runtime.md index 676c6c550..84b75ec29 100644 --- a/docfx/api/namespaces/Cuemon.Runtime.md +++ b/docfx/api/namespaces/Cuemon.Runtime.md @@ -2,4 +2,17 @@ uid: Cuemon.Runtime summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Runtime namespace contains types that support different namespaces such as the Cuemon, Cuemon.Data, Cuemon.Net, and the Cuemon.Runtime.Caching namespaces. The namespace is an addition to the System.Runtime namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Runtime namespace](https://docs.microsoft.com/en-us/dotnet/api/system.runtime?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Runtime)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core/Runtime)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core/Runtime) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Security.Cryptography.md b/docfx/api/namespaces/Cuemon.Security.Cryptography.md index acc329462..c40e6ed9a 100644 --- a/docfx/api/namespaces/Cuemon.Security.Cryptography.md +++ b/docfx/api/namespaces/Cuemon.Security.Cryptography.md @@ -6,4 +6,13 @@ The Cuemon.Security.Cryptography namespace contains types related to cryptograph Availability: NET Standard 2.0 -Complements: [System.Security.Cryptography namespace](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography?view=netstandard-2.0) \ No newline at end of file +Complements: [System.Security.Cryptography namespace](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Security.Cryptography)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Security.Cryptography)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Security.Cryptography) + +NuGet packages 📦\ +[Cuemon.Security.Cryptography (CI)](https://nuget.cuemon.net/packages/Cuemon.Security.Cryptography)\ +[Cuemon.Security.Cryptography (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Security.Cryptography) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Security.md b/docfx/api/namespaces/Cuemon.Security.md index a0415171c..c96f92eab 100644 --- a/docfx/api/namespaces/Cuemon.Security.md +++ b/docfx/api/namespaces/Cuemon.Security.md @@ -2,4 +2,17 @@ uid: Cuemon.Security summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Security namespace contains types related to hashing (both non-cryptographic and CRC) and has the base class from which all implementations of hash algorithms and checksums should derive. The namespace is an addition to the System.Security namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Security namespace](https://docs.microsoft.com/en-us/dotnet/api/system.security?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Security)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core/Security)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core/Security) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Text.md b/docfx/api/namespaces/Cuemon.Text.md index 847d2a98b..110899806 100644 --- a/docfx/api/namespaces/Cuemon.Text.md +++ b/docfx/api/namespaces/Cuemon.Text.md @@ -2,4 +2,17 @@ uid: Cuemon.Text summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Text namespace contains types tailored to ease the pain of working with encodings, BOM, parsing, preamble sequences and stems. Also includes way to conform to a uniform way of turning strings into objects of a particular type. The namespace is an addition to the System.Text namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Text namespace](https://docs.microsoft.com/en-us/dotnet/api/system.text?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Text)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core/Text)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core/Text) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Threading.md b/docfx/api/namespaces/Cuemon.Threading.md index ea03a24e2..03f85f3ba 100644 --- a/docfx/api/namespaces/Cuemon.Threading.md +++ b/docfx/api/namespaces/Cuemon.Threading.md @@ -6,4 +6,15 @@ The Cuemon.Threading namespace contains types related to working with long-runni Availability: NET Standard 2.0 -Complements: [System.Threading namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading?view=netstandard-2.0) \ No newline at end of file +Complements: [System.Threading namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading?view=netstandard-2.0) 🔗 + +Related: [Cuemon.Extensions.Threading.Tasks namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Threading.Tasks.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Threading)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Threading)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Threading) + +NuGet packages 📦\ +[Cuemon.Threading (CI)](https://nuget.cuemon.net/packages/Cuemon.Threading)\ +[Cuemon.Threading (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Threading) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.Linq.md b/docfx/api/namespaces/Cuemon.Xml.Linq.md new file mode 100644 index 000000000..367fe8674 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Xml.Linq.md @@ -0,0 +1,20 @@ +--- +uid: Cuemon.Xml.Linq +summary: *content +--- +The Cuemon.Xml.Linq namespace contains types that is used internally by this and related assemblies and is not intended to be used directly from your code. The namespace is an addition to the System.Xml.Linq namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Xml.Linq namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.linq?view=netstandard-2.0) 🔗 + +Related: [Cuemon.Extensions.Xml.Linq namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Xml.Linq.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Xml/Extensions/Linq)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Xml/Extensions/Linq)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Xml/Extensions/Linq) + +NuGet packages 📦\ +[Cuemon.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Xml)\ +[Cuemon.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Xml) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md b/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md index c3977fb9e..0ab7c7f64 100644 --- a/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md +++ b/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md @@ -4,4 +4,15 @@ summary: *content --- The Cuemon.Xml.Serialization.Converters namespace contains types tailored to resemble the [JsonConverter](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonConverter.htm) except we convert objects to and from XML. -Availability: NET Standard 2.0 \ No newline at end of file +Availability: NET Standard 2.0 + +Related: [Cuemon.Extensions.Xml.Serialization.Converters namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Xml.Serialization.Converters.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xml/Serialization/Converters)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Xml/Serialization/Converters)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Xml/Serialization/Converters) + +NuGet packages 📦\ +[Cuemon.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Xml)\ +[Cuemon.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Xml) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md b/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md index bed1239dd..b6ff49267 100644 --- a/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md +++ b/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md @@ -4,4 +4,13 @@ summary: *content --- The Cuemon.Xml.Serialization.Formatters namespace contains types that are used to serialize and deserialize objects into and from XML format using a generic signature. -Availability: NET Standard 2.0 \ No newline at end of file +Availability: NET Standard 2.0 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Xml/Serialization/Formatters)\ +[release](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Xml/Serialization/Formatters)\ +[master](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Xml/Serialization/Formatters) + +NuGet packages 📦\ +[Cuemon.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Xml)\ +[Cuemon.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Xml) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.Serialization.md b/docfx/api/namespaces/Cuemon.Xml.Serialization.md index 196f2d52b..fcfc0dd96 100644 --- a/docfx/api/namespaces/Cuemon.Xml.Serialization.md +++ b/docfx/api/namespaces/Cuemon.Xml.Serialization.md @@ -6,4 +6,15 @@ The Cuemon.Xml.Serialization namespace contains types that are used to serialize Availability: NET Standard 2.0 -Complements: [System.Xml.Serialization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization?view=netstandard-2.0) \ No newline at end of file +Complements: [System.Xml.Serialization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization?view=netstandard-2.0) 🔗 + +Related: [Cuemon.Extensions.Xml.Serialization namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Xml.Serialization.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Xml/Serialization)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Xml/Serialization)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Xml/Serialization) + +NuGet packages 📦\ +[Cuemon.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Xml)\ +[Cuemon.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Xml) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.XPath.md b/docfx/api/namespaces/Cuemon.Xml.XPath.md index 0c8931e16..c639bfa14 100644 --- a/docfx/api/namespaces/Cuemon.Xml.XPath.md +++ b/docfx/api/namespaces/Cuemon.Xml.XPath.md @@ -6,4 +6,13 @@ The Cuemon.Xml.XPath namespace contains types related to easing creation of XPat Availability: NET Standard 2.0 -Complements: [System.Xml.XPath namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.xpath?view=netstandard-2.0) \ No newline at end of file +Complements: [System.Xml.XPath namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.xpath?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Xml/XPath)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Xml/XPath)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Xml/XPath) + +NuGet packages 📦\ +[Cuemon.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Xml)\ +[Cuemon.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Xml) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Xml.md b/docfx/api/namespaces/Cuemon.Xml.md index a84af953b..785cc3417 100644 --- a/docfx/api/namespaces/Cuemon.Xml.md +++ b/docfx/api/namespaces/Cuemon.Xml.md @@ -6,4 +6,15 @@ The Cuemon.Xml namespace contains types related to encoding, converting and seri Availability: NET Standard 2.0 -Complements: [System.Xml namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml?view=netstandard-2.0), [System.Xml.Serialization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization?view=netstandard-2.0) \ No newline at end of file +Complements: [System.Xml namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml?view=netstandard-2.0), [System.Xml.Serialization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization?view=netstandard-2.0) 🔗 + +Related: [Cuemon.Extensions.Xml namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Xml.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Xml)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Xml)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Xml) + +NuGet packages 📦\ +[Cuemon.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Xml)\ +[Cuemon.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Xml) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.md b/docfx/api/namespaces/Cuemon.md index 8d130fa46..cdd0a71a8 100644 --- a/docfx/api/namespaces/Cuemon.md +++ b/docfx/api/namespaces/Cuemon.md @@ -2,4 +2,19 @@ uid: Cuemon summary: *content --- -The Cuemon namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon namespace contains fundamental types such as value and reference types, factories and utility classes, interfaces, attributes and feature rich delegates to support functional programming to a whole new level. The namespace is an addition to the System namespace. + +Availability: NET Standard 2.0 + +Complements: [System namespace](https://docs.microsoft.com/en-us/dotnet/api/system?view=netstandard-2.0) 🔗 + +Related: [Cuemon.Extensions namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Core)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Core) + +NuGet packages 📦\ +[Cuemon.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Core)\ +[Cuemon.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Core) \ No newline at end of file diff --git a/docfx/templates/cuemon/index.html.tmpl b/docfx/templates/cuemon/index.html.tmpl index 64dd4db04..6953b6855 100644 --- a/docfx/templates/cuemon/index.html.tmpl +++ b/docfx/templates/cuemon/index.html.tmpl @@ -10,7 +10,9 @@
{{{conceptual}}} + {{^_disableFooter}} {{>partials/footer}} + {{/_disableFooter}}
{{>partials/scripts}} From 922fdbc867f4919403873313a451aeb7814782ea Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 24 Sep 2020 23:19:09 +0200 Subject: [PATCH 217/385] Removed implements. --- src/Cuemon.Core/Text/IConfigurableParser.cs | 1 - .../Cuemon.Extensions.Xunit.Hosting.csproj | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Cuemon.Core/Text/IConfigurableParser.cs b/src/Cuemon.Core/Text/IConfigurableParser.cs index 912ffa723..f0060087d 100644 --- a/src/Cuemon.Core/Text/IConfigurableParser.cs +++ b/src/Cuemon.Core/Text/IConfigurableParser.cs @@ -4,7 +4,6 @@ namespace Cuemon.Text { /// /// Defines methods that converts a to an of a particular type. - /// Implements the /// /// The type of the delegate setup. /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj index a431f7ef8..31e9d5b2d 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Xunit.Hosting Cuemon.Extensions.Xunit.Hosting - The Cuemon.Extensions.Xunit.Hosting namespace contains types that provides a uniform way of doing unit testing that is used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. + The Cuemon.Extensions.Xunit.Hosting namespace contains types that provides a uniform way of doing unit testing used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. host-test class-fixture host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services From 33efee28edd790b8fde5cf213b5803c661a44593 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 25 Sep 2020 00:32:39 +0200 Subject: [PATCH 218/385] DocFx namespace descriptions. --- docfx/api/namespaces/Cuemon.Extensions.Xml.md | 28 ++++++++++++++++++- .../Cuemon.Extensions.Xunit.Hosting.md | 2 +- docfx/api/namespaces/Cuemon.Xml.md | 4 +-- .../Cuemon.Extensions.Xml.csproj | 2 +- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.md index bc715d299..de55bad1c 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.md @@ -2,4 +2,30 @@ uid: Cuemon.Extensions.Xml summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Xml namespace contains extension methods that complements the Cuemon.Xml namespace while being an addition to the System.Xml namespace. + +Availability: NET Standard 2.0 + +Related: [Cuemon.Xml namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xml)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Xml)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Xml) + +NuGet packages 📦\ +[Cuemon.Extensions.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Xml)\ +[Cuemon.Extensions.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Xml) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|byte[]|⬇️|`ToXmlReader`| +|DateTime|⬇️|`ToString`| +|IHierarchy|⬇️|`HasXmlIgnoreAttribute`, `IsNodeEnumerable`, `GetXmlRootOrElement`, `OrderByXmlAttributes`| +|Stream|⬇️|`ToXmlReader`, `CopyXmlStream`, `TryDetectXmlEncoding`, `RemoveXmlNamespaceDeclarations`| +|String|⬇️|`EscapeXml`, `UnescapeXml`, `SanitizeXmlElementName`, `SanitizeXmlElementText`| +|Uri|⬇️|`ToXmlReader`| +|XmlReader|⬇️|`Chunk`, `ToHierarchy`, `ToStream`, `MoveToFirstElement`| +|XmlWriter|⬇️|`WriteObject`, `WriteStartElement`, `WriteEncapsulatingElementWhenNotNull`, `WriteXmlRootElement`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md index 11ab0bfb6..9a823f8ba 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md @@ -2,7 +2,7 @@ uid: Cuemon.Extensions.Xunit.Hosting summary: *content --- -The Cuemon.Extensions.Xunit.Hosting namespace contains types that provides a uniform way of doing unit testing that is used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. +The Cuemon.Extensions.Xunit.Hosting namespace contains types that provides a uniform way of doing unit testing that is used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. Availability: NET Standard 2.0, NET Core 3.0 diff --git a/docfx/api/namespaces/Cuemon.Xml.md b/docfx/api/namespaces/Cuemon.Xml.md index 785cc3417..61e7ef534 100644 --- a/docfx/api/namespaces/Cuemon.Xml.md +++ b/docfx/api/namespaces/Cuemon.Xml.md @@ -2,11 +2,11 @@ uid: Cuemon.Xml summary: *content --- -The Cuemon.Xml namespace contains types related to encoding, converting and serialization. The included lightweight XML serializer framework offers same flexibility as the one provided by the JSON equivalent from Newtonsoft. The namespace is an addition to both System.Xml- and System.Xml.Serialization namespaces. +The Cuemon.Xml namespace contains types related to encoding, converting and serialization. The included lightweight XML serializer framework offers same flexibility as the one provided by the JSON equivalent from Newtonsoft. The namespace is an addition to the System.Xml namespace. Availability: NET Standard 2.0 -Complements: [System.Xml namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml?view=netstandard-2.0), [System.Xml.Serialization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization?view=netstandard-2.0) 🔗 +Complements: [System.Xml namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml?view=netstandard-2.0) 🔗 Related: [Cuemon.Extensions.Xml namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Xml.html) 📘 diff --git a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj index e0335c8ed..f237108a1 100644 --- a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj +++ b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj @@ -9,7 +9,7 @@ Cuemon Cuemon.Extensions.Xml Cuemon.Extensions.Xml - The Cuemon.Extensions.Xml namespace contains extension methods and features related to the System.Xml namespace that provides access to XML scenarios such as escaping, conversions, parsing, sanitizing, serialization and deserialization. + The Cuemon.Extensions.Xml namespace contains extension methods related to the System.Xml namespace that provides access to XML scenarios such as escaping, conversions, parsing, sanitizing, serialization and deserialization. extension-methods extensions to-xml-reader copy-xml-stream try-detect-xml-encoding chunk to-stream write-object From 72fe35ec256a4b9ac6dba23abe0ff483d7210fd8 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 25 Sep 2020 00:51:33 +0200 Subject: [PATCH 219/385] DocFx namespace descriptions. --- .../namespaces/Cuemon.Extensions.Xml.Linq.md | 19 ++++++++++++++++- ...Extensions.Xml.Serialization.Converters.md | 21 ++++++++++++++++++- ...xtensions.Xml.Serialization.Diagnostics.md | 21 ++++++++++++++++++- .../Cuemon.Extensions.Xml.Serialization.md | 21 ++++++++++++++++++- 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md index 3a71857f1..13b9a1060 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md @@ -2,4 +2,21 @@ uid: Cuemon.Extensions.Xml.Linq summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Xml.Linq namespace contains extension methods that complements the System namespace while being an addition to the System.Xml.Linq namespace. + +Availability: NET Standard 2.0 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xml/Linq)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Xml/Linq)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Xml/Linq) + +NuGet packages 📦\ +[Cuemon.Extensions.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Xml)\ +[Cuemon.Extensions.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Xml) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|String|⬇️|`IsXmlString`, `TryParseXElement`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md index bb3791149..7d15338b0 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.Xml.Serialization.Converters summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Xml.Serialization.Converters namespace contains extension methods that complements the Cuemon.Xml.Serialization.Converters namespace. + +Availability: NET Standard 2.0 + +Related: [Cuemon.Xml.Serialization.Converters namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.Serialization.Converters.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xml/Serialization/Converters)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Xml/Serialization/Converters)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Xml/Serialization/Converters) + +NuGet packages 📦\ +[Cuemon.Extensions.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Xml)\ +[Cuemon.Extensions.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Xml) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IList{XmlConverter}|⬇️|`FirstOrDefaultReaderConverter`, `FirstOrDefaultWriterConverter`, `AddXmlConverter`, `InsertXmlConverter`, `AddEnumerableConverter`, `AddExceptionDescriptorConverter`, `AddUriConverter`, `AddDateTimeConverter`, `AddTimeSpanConverter`, `AddStringConverter`, `AddExceptionConverter`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md index 7423aaffc..11092fc80 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.Xml.Serialization.Diagnostics summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Xml.Serialization.Diagnostics namespace contains extension methods that complements the Cuemon.Diagnostics namespace. + +Availability: NET Standard 2.0 + +Related: [Cuemon.Diagnostics namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xml/Serialization/Diagnostics)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Xml/Serialization/Diagnostics)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Xml/Serialization/Diagnostics) + +NuGet packages 📦\ +[Cuemon.Extensions.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Xml)\ +[Cuemon.Extensions.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Xml) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|ExceptionDescriptor|⬇️|`ToInsightsXmlString`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md index f95d50485..2428929d4 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.Xml.Serialization summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Xml.Serialization namespace contains extension methods that complements the Cuemon.Xml.Serialization namespace. + +Availability: NET Standard 2.0 + +Related: [Cuemon.Xml.Serialization namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.Serialization.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xml/Serialization)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Xml/Serialization)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Xml/Serialization) + +NuGet packages 📦\ +[Cuemon.Extensions.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Xml)\ +[Cuemon.Extensions.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Xml) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|XmlSerializerOptions|⬇️|`ApplyToDefaultSettings`| \ No newline at end of file From 31264d2cb793e17111cffd78b6a18177b720c3a2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 25 Sep 2020 01:36:02 +0200 Subject: [PATCH 220/385] Updated package release notes and DocFx namespace description. --- docfx/api/namespaces/Cuemon.Extensions.Xml.md | 4 ++-- .../Cuemon.Extensions.Xml.csproj | 4 ++-- .../Properties/PackageReleaseNotes.txt | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.md index de55bad1c..f3bef0d37 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.md @@ -23,9 +23,9 @@ NuGet packages 📦\ |--:|:-:|---| |byte[]|⬇️|`ToXmlReader`| |DateTime|⬇️|`ToString`| -|IHierarchy|⬇️|`HasXmlIgnoreAttribute`, `IsNodeEnumerable`, `GetXmlRootOrElement`, `OrderByXmlAttributes`| +|IHierarchy{T}|⬇️|`HasXmlIgnoreAttribute`, `IsNodeEnumerable`, `GetXmlRootOrElement`, `OrderByXmlAttributes`| |Stream|⬇️|`ToXmlReader`, `CopyXmlStream`, `TryDetectXmlEncoding`, `RemoveXmlNamespaceDeclarations`| |String|⬇️|`EscapeXml`, `UnescapeXml`, `SanitizeXmlElementName`, `SanitizeXmlElementText`| |Uri|⬇️|`ToXmlReader`| |XmlReader|⬇️|`Chunk`, `ToHierarchy`, `ToStream`, `MoveToFirstElement`| -|XmlWriter|⬇️|`WriteObject`, `WriteStartElement`, `WriteEncapsulatingElementWhenNotNull`, `WriteXmlRootElement`| \ No newline at end of file +|XmlWriter|⬇️|`WriteObject`, `WriteObject{T}`, `WriteStartElement`, `WriteEncapsulatingElementWhenNotNull{T}`, `WriteXmlRootElement{T}`| \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj index f237108a1..ff4f6d4f5 100644 --- a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj +++ b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj @@ -9,8 +9,8 @@ Cuemon Cuemon.Extensions.Xml Cuemon.Extensions.Xml - The Cuemon.Extensions.Xml namespace contains extension methods related to the System.Xml namespace that provides access to XML scenarios such as escaping, conversions, parsing, sanitizing, serialization and deserialization. - extension-methods extensions to-xml-reader copy-xml-stream try-detect-xml-encoding chunk to-stream write-object + The Cuemon.Extensions.Xml namespace contains extension methods that complements the Cuemon.Xml namespace while being an addition to the System.Xml namespace. + extension-methods extensions to-xml-reader copy-xml-stream try-detect-xml-encoding chunk to-stream write-object is-xml-string try-parse-xelement diff --git a/src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..596021e79 --- /dev/null +++ b/src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt @@ -0,0 +1,17 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# Breaking Changes +- RENAMED Copy --> ToStream on the XmlReaderExtensions class in the Cuemon.Xml namespace (also removed generic type parameters) +  +# New Features +- ADDED XmlConverterExtensions class in the Cuemon.Extensions.Xml.Serialization.Converters namespace that consist of extension methods for the IList{XmlConverter} interface: FirstOrDefaultReaderConverter, FirstOrDefaultWriterConverter, AddXmlConverter, InsertXmlConverter, AddEnumerableConverter, AddExceptionDescriptorConverter, AddUriConverter, AddDateTimeConverter, AddTimeSpanConverter, AddStringConverter, AddExceptionConverter +- ADDED ExceptionDescriptorExtensions class in the Cuemon.Extensions.Xml.Serialization.Diagnostics namespace that consist of extension methods for the ExceptionDescriptor class: ToInsightsXmlString +- ADDED XmlSerializerOptionsExtensions class in the Cuemon.Extensions.Xml.Serialization namespace that consist of extension methods for the XmlSerializerOptions class: ApplyToDefaultSettings +- ADDED ByteArrayExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the byte[] struct: ToXmlReader +- ADDED HierarchyExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the IHierarchy{T} interface: IsNodeEnumerable, GetXmlRootOrElement, OrderByXmlAttributes +- ADDED StreamExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the Stream class: ToXmlReader, CopyXmlStream, TryDetectXmlEncoding +- ADDED UriExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the Uri struct: ToXmlReader +- ADDED XmlReaderExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the XmlReader class: ToHierarchy, MoveToFirstElement +- ADDED XmlWriterExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the XmlWriter class: WriteObject, WriteObject{T}, WriteStartElement, WriteEncapsulatingElementWhenNotNull{T}, WriteXmlRootElement{T} +  \ No newline at end of file From c2e129d46aed1d398bec9a865b5d4be66a356e8e Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 25 Sep 2020 19:21:19 +0200 Subject: [PATCH 221/385] Updated package release notes and DocFx namespace descriptions. --- .../Cuemon.Extensions.Threading.Tasks.md | 21 ++++++++++++++++++- .../namespaces/Cuemon.Extensions.Threading.md | 15 ++++++++++++- .../Cuemon.Extensions.Threading.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 6 ++++++ .../Tasks/TaskExtensions.cs | 2 +- 5 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 src/Cuemon.Extensions.Threading/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md b/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md index b59ed5748..9a808309f 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.Threading.Tasks summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Threading.Tasks namespace contains extension methods that complements the System.Threading.Tasks namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Threading.Tasks namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Threading/Tasks)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Threading/Tasks)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Threading/Tasks) + +NuGet packages 📦\ +[Cuemon.Threading (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Threading)\ +[Cuemon.Threading (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Threading) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|Task|⬇️|`ContinueWithCapturedContext`, `ContinueWithCapturedContext{TResult}`, `ContinueWithSuppressedContext`, `ContinueWithSuppressedContext{TResult}`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Threading.md b/docfx/api/namespaces/Cuemon.Extensions.Threading.md index d0116856c..69303fa32 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Threading.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Threading.md @@ -2,4 +2,17 @@ uid: Cuemon.Extensions.Threading summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Threading namespace contains extension methods that complements the System.Threading namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Threading namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Threading)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Threading)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Threading) + +NuGet packages 📦\ +[Cuemon.Threading (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Threading)\ +[Cuemon.Threading (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Threading) \ No newline at end of file diff --git a/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj b/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj index 37edbeeb2..4259a0cea 100644 --- a/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj +++ b/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Threading Cuemon.Extensions.Threading - The Cuemon.Extensions.Threading namespace contains extension methods and features related to the System.Threading namespace. + The Cuemon.Extensions.Threading namespace contains extension methods that complements the System.Threading namespace. extension-methods extensions continue-with-captured-context continue-with-suppressed-context diff --git a/src/Cuemon.Extensions.Threading/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Threading/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..774c16ea8 --- /dev/null +++ b/src/Cuemon.Extensions.Threading/Properties/PackageReleaseNotes.txt @@ -0,0 +1,6 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED TaskExtensions class in the Cuemon.Extensions.Threading.Tasks namespace that consist of extension methods for the Task class: ContinueWithCapturedContext, ContinueWithCapturedContext{TResult}, ContinueWithSuppressedContext, ContinueWithSuppressedContext{TResult} +  \ No newline at end of file diff --git a/src/Cuemon.Extensions.Threading/Tasks/TaskExtensions.cs b/src/Cuemon.Extensions.Threading/Tasks/TaskExtensions.cs index a7a9d6e04..e9d6d57e2 100644 --- a/src/Cuemon.Extensions.Threading/Tasks/TaskExtensions.cs +++ b/src/Cuemon.Extensions.Threading/Tasks/TaskExtensions.cs @@ -4,7 +4,7 @@ namespace Cuemon.Extensions.Threading.Tasks { /// - /// Extension methods for the . + /// Extension methods for the class. /// public static class TaskExtensions { From 384c8a12c2443d336e005b471ef9d64bd7eed56e Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 25 Sep 2020 19:35:50 +0200 Subject: [PATCH 222/385] Updated package release notes and DocFx namespace description. --- .../api/namespaces/Cuemon.Extensions.Text.md | 22 ++++++++++++++++++- .../Cuemon.Extensions.Threading.Tasks.md | 6 ++--- .../namespaces/Cuemon.Extensions.Threading.md | 6 ++--- .../Cuemon.Extensions.Text.csproj | 2 +- .../EncodingOptionsExtensions.cs | 2 +- .../Properties/PackageReleaseNotes.txt | 7 ++++++ .../Cuemon.Extensions.Xml.csproj | 1 - 7 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 src/Cuemon.Extensions.Text/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.Text.md b/docfx/api/namespaces/Cuemon.Extensions.Text.md index 7cf0df37c..959b047d6 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Text.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Text.md @@ -2,4 +2,24 @@ uid: Cuemon.Extensions.Text summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Text namespace contains extension methods that complements the Cuemon.Text namespace while being an addition to the System namespace. + +Availability: NET Standard 2.0 + +Related: [Cuemon.Text namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Text.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Text)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Text)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Text) + +NuGet packages 📦\ +[Cuemon.Text (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Text)\ +[Cuemon.Text (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Text) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IEncodingOptions|⬇️|`DetectUnicodeEncoding`| +|String|⬇️|`ToEncodedString`, `ToAsciiEncodedString`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md b/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md index 9a808309f..61e38df75 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md @@ -6,16 +6,14 @@ The Cuemon.Extensions.Threading.Tasks namespace contains extension methods that Availability: NET Standard 2.0 -Complements: [System.Threading.Tasks namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks?view=netstandard-2.0) 🔗 - Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Threading/Tasks)\ [release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Threading/Tasks)\ [master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Threading/Tasks) NuGet packages 📦\ -[Cuemon.Threading (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Threading)\ -[Cuemon.Threading (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Threading) +[Cuemon.Extensions.Threading (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Threading)\ +[Cuemon.Extensions.Threading (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Threading) ### Extension Methods diff --git a/docfx/api/namespaces/Cuemon.Extensions.Threading.md b/docfx/api/namespaces/Cuemon.Extensions.Threading.md index 69303fa32..23578c928 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Threading.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Threading.md @@ -6,13 +6,11 @@ The Cuemon.Extensions.Threading namespace contains extension methods that comple Availability: NET Standard 2.0 -Complements: [System.Threading namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading?view=netstandard-2.0) 🔗 - Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Threading)\ [release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Threading)\ [master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Threading) NuGet packages 📦\ -[Cuemon.Threading (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Threading)\ -[Cuemon.Threading (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Threading) \ No newline at end of file +[Cuemon.Extensions.Threading (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Threading)\ +[Cuemon.Extensions.Threading (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Threading) \ No newline at end of file diff --git a/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj b/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj index 96c956dd0..2428af84d 100644 --- a/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj +++ b/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Text Cuemon.Extensions.Text - The Cuemon.Extensions.Text namespace contains extension methods and features related to the Cuemon.Text namespace. + The Cuemon.Extensions.Text namespace contains extension methods that complements the Cuemon.Text namespace while being an addition to the System namespace. extension-methods extensions to-encoded-string to-ascii-encoded-string diff --git a/src/Cuemon.Extensions.Text/EncodingOptionsExtensions.cs b/src/Cuemon.Extensions.Text/EncodingOptionsExtensions.cs index ca5bd7831..5c741ae31 100644 --- a/src/Cuemon.Extensions.Text/EncodingOptionsExtensions.cs +++ b/src/Cuemon.Extensions.Text/EncodingOptionsExtensions.cs @@ -5,7 +5,7 @@ namespace Cuemon.Extensions.Text { /// - /// Extension methods for the class. + /// Extension methods for the interface. /// public static class EncodingOptionsExtensions { diff --git a/src/Cuemon.Extensions.Text/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Text/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..97742e289 --- /dev/null +++ b/src/Cuemon.Extensions.Text/Properties/PackageReleaseNotes.txt @@ -0,0 +1,7 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED EncodingOptionsExtensions class in the Cuemon.Extensions.Text namespace that consist of extension methods for the EncodingOptionsExtensions class: DetectUnicodeEncoding +- ADDED StringExtensions class in the Cuemon.Extensions.Text namespace that consist of extension methods for the string class: ToEncodedString, ToAsciiEncodedString +  \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj index ff4f6d4f5..b637bc475 100644 --- a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj +++ b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj @@ -6,7 +6,6 @@ - Cuemon Cuemon.Extensions.Xml Cuemon.Extensions.Xml The Cuemon.Extensions.Xml namespace contains extension methods that complements the Cuemon.Xml namespace while being an addition to the System.Xml namespace. From ae7a44beace4a9e10df1b906de32b9bee448ca9d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 25 Sep 2020 19:46:28 +0200 Subject: [PATCH 223/385] Updated DocFx namespace descriptions. --- .../Cuemon.Extensions.Runtime.Caching.md | 19 +++++++++++++++++-- .../api/namespaces/Cuemon.Extensions.Text.md | 2 +- ...Extensions.Xml.Serialization.Converters.md | 2 +- ...xtensions.Xml.Serialization.Diagnostics.md | 2 +- .../Cuemon.Extensions.Xml.Serialization.md | 2 +- docfx/api/namespaces/Cuemon.Extensions.Xml.md | 2 +- 6 files changed, 22 insertions(+), 7 deletions(-) diff --git a/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md b/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md index 3aa8df1f9..d7d188248 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md @@ -2,8 +2,23 @@ uid: Cuemon.Extensions.Runtime.Caching summary: *content --- -The Cuemon.Extensions.Runtime.Caching namespace contains extension methods that complements the Cuemon.Runtime.Caching namespace by adding support for Memoization techniques and GetOrAdd convenience; both with vast overloads and extended by the ICacheEnumerable{TKey} interface for loose coupling. +The Cuemon.Extensions.Runtime.Caching namespace contains extension methods that complements the Cuemon.Runtime.Caching namespace by adding support for Memoization techniques and GetOrAdd convenience ; both with vast overloads and extended by the ICacheEnumerable{TKey} interface for loose coupling. Availability: NET Standard 2.0 -Complements: [Cuemon.Runtime.Caching namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Runtime.Caching.html) \ No newline at end of file +Complements: [Cuemon.Runtime.Caching namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Runtime.Caching.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Runtime.Caching)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Runtime.Caching)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Runtime.Caching) + +NuGet packages 📦\ +[Cuemon.Extensions.Runtime.Caching (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Runtime.Caching)\ +[Cuemon.Extensions.Runtime.Caching (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Runtime.Caching) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|ICacheEnumerable{TKey}|⬇️|`GetOrAdd{TKey, TResult}`, `Memoize{TKey, T, TResult}`, `Memoize{TKey, T, TResult}`, `Memoize{TKey, T1, T2, TResult}`, `Memoize{TKey, T1, T2, T3, TResult}`, `Memoize{TKey, T1, T2, T3, T4, TResult}`, `Memoize{TKey, T1, T2, T3, T4, T5, TResult}`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Text.md b/docfx/api/namespaces/Cuemon.Extensions.Text.md index 959b047d6..acec8af04 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Text.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Text.md @@ -6,7 +6,7 @@ The Cuemon.Extensions.Text namespace contains extension methods that complements Availability: NET Standard 2.0 -Related: [Cuemon.Text namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Text.html) 📘 +Complements: [Cuemon.Text namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Text.html) 🔗 Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Text)\ diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md index 7d15338b0..5a9032be1 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md @@ -6,7 +6,7 @@ The Cuemon.Extensions.Xml.Serialization.Converters namespace contains extension Availability: NET Standard 2.0 -Related: [Cuemon.Xml.Serialization.Converters namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.Serialization.Converters.html) 📘 +Complements: [Cuemon.Xml.Serialization.Converters namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.Serialization.Converters.html) 🔗 Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xml/Serialization/Converters)\ diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md index 11092fc80..8121a6dfc 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md @@ -6,7 +6,7 @@ The Cuemon.Extensions.Xml.Serialization.Diagnostics namespace contains extension Availability: NET Standard 2.0 -Related: [Cuemon.Diagnostics namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.html) 📘 +Complements: [Cuemon.Diagnostics namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.html) 🔗 Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xml/Serialization/Diagnostics)\ diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md index 2428929d4..7585c1430 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md @@ -6,7 +6,7 @@ The Cuemon.Extensions.Xml.Serialization namespace contains extension methods tha Availability: NET Standard 2.0 -Related: [Cuemon.Xml.Serialization namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.Serialization.html) 📘 +Complements: [Cuemon.Xml.Serialization namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.Serialization.html) 🔗 Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xml/Serialization)\ diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.md index f3bef0d37..adae5c5c1 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.md @@ -6,7 +6,7 @@ The Cuemon.Extensions.Xml namespace contains extension methods that complements Availability: NET Standard 2.0 -Related: [Cuemon.Xml namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.html) 📘 +Complements: [Cuemon.Xml namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.html) 🔗 Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xml)\ From a3c71c8b1ace8851acfff4ef70af02e9231e671a Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 26 Sep 2020 00:59:49 +0200 Subject: [PATCH 224/385] Updated package description, release info and DocFx namespace descriptions. --- .../Cuemon.Extensions.Reflection.md | 24 ++++++++++++++++++- .../api/namespaces/Cuemon.Extensions.Text.md | 4 ++-- .../Properties/PackageReleaseNotes.txt | 5 +++- .../Cuemon.Extensions.Reflection.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 8 +++++++ 5 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 src/Cuemon.Extensions.Reflection/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.Reflection.md b/docfx/api/namespaces/Cuemon.Extensions.Reflection.md index 4ed221717..687cbc116 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Reflection.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Reflection.md @@ -2,4 +2,26 @@ uid: Cuemon.Extensions.Reflection summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Reflection namespace contains extension methods that complements the Cuemon.Reflection namespace while being an addition to the System.Reflection namespace. + +Availability: NET Standard 2.0 + +Complements: [Cuemon.Reflection namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Reflection.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Reflection)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Reflection)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Reflection) + +NuGet packages 📦\ +[Cuemon.Extensions.Reflection (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Reflection)\ +[Cuemon.Extensions.Reflection (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Reflection) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|Assembly|⬇️|`GetAssemblyVersion`, `GetFileVersion`, `GetProductVersion`, `IsDebugBuild`| +|MemberInfo|⬇️|`HasAttributes`| +|PropertyInfo|⬇️|`IsAutoProperty`| +|Type|⬇️|`GetEmbeddedResources`, `ToMethodBase`, `GetRuntimePropertiesExceptOf{T}`, `ToFullNameIncludingAssemblyName`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Text.md b/docfx/api/namespaces/Cuemon.Extensions.Text.md index acec8af04..f46016ef9 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Text.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Text.md @@ -14,8 +14,8 @@ Github branches 🌱\ [master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Text) NuGet packages 📦\ -[Cuemon.Text (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Text)\ -[Cuemon.Text (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Text) +[Cuemon.Extensions.Text (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Text)\ +[Cuemon.Extensions.Text (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Text) ### Extension Methods diff --git a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt index 4ba2906f2..b7ad227f2 100644 --- a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt @@ -3,7 +3,7 @@ Availability: NET Standard 2.0   # Upgrade Steps - To use the earlier built-in support for transient fault handling, please refer to the Cuemon.Resilience namespace, as it has been merged and refactored into this assembly -- +- Any former extension methods of the Cuemon namespace (and related) was either removed completely or merged into there respective Cuemon.Extensions.* namespace equivalent   # Breaking Changes - REMOVED StringFormatter class from the Cuemon namespace @@ -19,6 +19,9 @@ Availability: NET Standard 2.0 - REMOVED SecurityToken class from the Cuemon.Security namespace - REMOVED SecurityTokenSettings class from the Cuemon.Security namespace (replaced with SignedUriOptions in the Cuemon.Extensions.Net.Security namespace) - REMOVED SecurityUtility class from the Cuemon.Security namespace +- REMOVED AssemblyExtensions class from the Cuemon.Reflection namespace +- MOVED MemberInfoExtensions class from the Cuemon.Reflection namespace to Cuemon.Extensions.Reflection namespace +- REMOVED MethodBaseConverterExtensions class from the Cuemon.Reflection namespace - MOVED LatencyException class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) - MOVED TransientOperation class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) - MOVED TransientFaultEvidence class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) diff --git a/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj b/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj index 5200940eb..510b19d3a 100644 --- a/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj +++ b/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Reflection Cuemon.Extensions.Reflection - The Cuemon.Extensions.Reflection namespace contains extension methods and features related to the System.Reflection namespace. + The Cuemon.Extensions.Reflection namespace contains extension methods that complements the Cuemon.Reflection namespace while being an addition to the System.Reflection namespace. extension-methods extensions get-assembly-version get-file-version get-product-version is-debug-build has-attributes is-auto-property get-runtime-properties-except-of diff --git a/src/Cuemon.Extensions.Reflection/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Reflection/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..a24eb3c74 --- /dev/null +++ b/src/Cuemon.Extensions.Reflection/Properties/PackageReleaseNotes.txt @@ -0,0 +1,8 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED AssemblyExtensions class in the Cuemon.Extensions.Reflection namespace that consist of extension methods for the Assembly class: GetAssemblyVersion, GetFileVersion, GetProductVersion, IsDebugBuild +- ADDED PropertyInfoExtensions class in the Cuemon.Extensions.Reflection namespace that consist of extension methods for the PropertyInfo class: IsAutoProperty +- ADDED TypeExtensions class in the Cuemon.Extensions.Reflection namespace that consist of extension methods for the Type class: GetEmbeddedResources, ToMethodBase, GetRuntimePropertiesExceptOf{T}, ToFullNameIncludingAssemblyName +  \ No newline at end of file From bacd16e8e0a707d70cfdd380db2337990c8915aa Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 28 Sep 2020 21:22:24 +0200 Subject: [PATCH 225/385] Changed signature to match the one in the JSON variant (eg. focus on XmlFormatter and XmlFormatterOptions incl. DefaultConverters). --- .../Bootstrapper.cs | 5 ++- .../Converters/XmlConverterExtensions.cs | 2 +- .../XmlWriterExtensions.cs | 9 ++--- .../XmlConverterDecoratorExtensions.cs | 4 +-- .../XmlWriterDecoratorExtensions.cs | 13 +++---- .../Serialization/Formatters/XmlFormatter.cs | 16 +++++++++ .../Formatters/XmlFormatterOptions.cs | 34 +++++++++++++------ src/Cuemon.Xml/Serialization/XmlSerializer.cs | 2 +- .../Serialization/XmlSerializerOptions.cs | 9 +---- 9 files changed, 58 insertions(+), 36 deletions(-) diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Bootstrapper.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Bootstrapper.cs index 8332804f9..8b3675953 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Bootstrapper.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Bootstrapper.cs @@ -1,5 +1,5 @@ using Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters; -using Cuemon.Xml.Serialization; +using Cuemon.Xml.Serialization.Formatters; namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml { @@ -17,7 +17,7 @@ internal static void Initialize() if (!_initialized) { _initialized = true; - XmlSerializerOptions.DefaultConverters += list => + XmlFormatterOptions.DefaultConverters += list => { list.AddHttpExceptionDescriptorConverter() .AddStringValuesConverter() @@ -28,7 +28,6 @@ internal static void Initialize() }; } } - } } } diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs index 126458c53..eb3ec3fea 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs @@ -48,7 +48,7 @@ public static IList AddHttpExceptionDescriptorConverter(this IList foreach (var evidence in descriptor.Evidence) { if (evidence.Value == null) { continue; } - writer.WriteObject(evidence.Value, evidence.Value.GetType(), o => o.RootName = new XmlQualifiedEntity(evidence.Key)); + writer.WriteObject(evidence.Value, evidence.Value.GetType(), o => o.Settings.RootName = new XmlQualifiedEntity(evidence.Key)); } writer.WriteEndElement(); } diff --git a/src/Cuemon.Extensions.Xml/XmlWriterExtensions.cs b/src/Cuemon.Extensions.Xml/XmlWriterExtensions.cs index dca6cafa8..589575f0c 100644 --- a/src/Cuemon.Extensions.Xml/XmlWriterExtensions.cs +++ b/src/Cuemon.Extensions.Xml/XmlWriterExtensions.cs @@ -2,6 +2,7 @@ using System.Xml; using Cuemon.Xml; using Cuemon.Xml.Serialization; +using Cuemon.Xml.Serialization.Formatters; namespace Cuemon.Extensions.Xml { @@ -16,11 +17,11 @@ public static class XmlWriterExtensions /// The type of the object to serialize. /// The to extend. /// The object to serialize. - /// The which need to be configured. + /// The which may be configured. /// /// cannot be null. /// - public static void WriteObject(this XmlWriter writer, T value, Action setup = null) + public static void WriteObject(this XmlWriter writer, T value, Action setup = null) { WriteObject(writer, value, typeof(T), setup); } @@ -31,11 +32,11 @@ public static void WriteObject(this XmlWriter writer, T value, ActionThe to extend. /// The object to serialize. /// The type of the object to serialize. - /// The which need to be configured. + /// The which may be configured. /// /// cannot be null. /// - public static void WriteObject(this XmlWriter writer, object value, Type objectType, Action setup = null) + public static void WriteObject(this XmlWriter writer, object value, Type objectType, Action setup = null) { Validator.ThrowIfNull(writer, nameof(writer)); Decorator.Enclose(writer).WriteObject(value, objectType, setup); diff --git a/src/Cuemon.Xml/Extensions/Serialization/Converters/XmlConverterDecoratorExtensions.cs b/src/Cuemon.Xml/Extensions/Serialization/Converters/XmlConverterDecoratorExtensions.cs index 04fe1596c..b9f996d06 100644 --- a/src/Cuemon.Xml/Extensions/Serialization/Converters/XmlConverterDecoratorExtensions.cs +++ b/src/Cuemon.Xml/Extensions/Serialization/Converters/XmlConverterDecoratorExtensions.cs @@ -185,7 +185,7 @@ public static IDecorator> AddExceptionDescriptorConverter(th foreach (var evidence in descriptor.Evidence) { if (evidence.Value == null) { continue; } - Decorator.Enclose(writer).WriteObject(evidence.Value, evidence.Value.GetType(), o => o.RootName = new XmlQualifiedEntity(evidence.Key)); + Decorator.Enclose(writer).WriteObject(evidence.Value, evidence.Value.GetType(), o => o.Settings.RootName = new XmlQualifiedEntity(evidence.Key)); } writer.WriteEndElement(); } @@ -343,7 +343,7 @@ private static void WriteExceptionCore(XmlWriter writer, Exception exception, bo { var value = property.GetValue(exception); if (value == null) { continue; } - Decorator.Enclose(writer).WriteObject(value, value.GetType(), settings => settings.RootName = new XmlQualifiedEntity(property.Name)); + Decorator.Enclose(writer).WriteObject(value, value.GetType(), o => o.Settings.RootName = new XmlQualifiedEntity(property.Name)); } WriteInnerExceptions(writer, exception, includeStackTrace); diff --git a/src/Cuemon.Xml/Extensions/XmlWriterDecoratorExtensions.cs b/src/Cuemon.Xml/Extensions/XmlWriterDecoratorExtensions.cs index 27470ec99..f4d4b6df7 100644 --- a/src/Cuemon.Xml/Extensions/XmlWriterDecoratorExtensions.cs +++ b/src/Cuemon.Xml/Extensions/XmlWriterDecoratorExtensions.cs @@ -5,6 +5,7 @@ using Cuemon.Reflection; using Cuemon.Runtime.Serialization; using Cuemon.Xml.Serialization; +using Cuemon.Xml.Serialization.Formatters; namespace Cuemon.Xml { @@ -21,11 +22,11 @@ public static class XmlWriterDecoratorExtensions /// The type of the object to serialize. /// The to extend. /// The object to serialize. - /// The which need to be configured. + /// The which may be configured. /// /// cannot be null. /// - public static void WriteObject(this IDecorator decorator, T value, Action setup = null) + public static void WriteObject(this IDecorator decorator, T value, Action setup = null) { WriteObject(decorator, value, typeof(T), setup); } @@ -36,15 +37,15 @@ public static void WriteObject(this IDecorator decorator, T value, /// The to extend. /// The object to serialize. /// The type of the object to serialize. - /// The which need to be configured. + /// The which may be configured. /// /// cannot be null. /// - public static void WriteObject(this IDecorator decorator, object value, Type objectType, Action setup = null) + public static void WriteObject(this IDecorator decorator, object value, Type objectType, Action setup = null) { Validator.ThrowIfNull(decorator, nameof(decorator)); - var serializer = XmlSerializer.Create(setup == null ? null : Patterns.Configure(setup)); - serializer.Serialize(decorator.Inner, value, objectType); + var formatter = new XmlFormatter(setup); + formatter.SerializeToWriter(decorator.Inner, value, objectType); } /// diff --git a/src/Cuemon.Xml/Serialization/Formatters/XmlFormatter.cs b/src/Cuemon.Xml/Serialization/Formatters/XmlFormatter.cs index 10c10baaf..78fe46743 100644 --- a/src/Cuemon.Xml/Serialization/Formatters/XmlFormatter.cs +++ b/src/Cuemon.Xml/Serialization/Formatters/XmlFormatter.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Xml; using Cuemon.Runtime.Serialization.Formatters; using Cuemon.Xml.Serialization.Converters; @@ -58,6 +59,21 @@ public override Stream Serialize(object source, Type objectType) return serializer.Serialize(source, objectType); } + /// + /// Serializes the specified into an XML format. + /// + /// The writer used in the serialization process. + /// The object to serialize to XML format. + /// The type of the object to serialize. + /// A stream of the serialized . + public void SerializeToWriter(XmlWriter writer, object source, Type objectType) + { + Validator.ThrowIfNull(source, nameof(source)); + Validator.ThrowIfNull(objectType, nameof(objectType)); + var serializer = XmlSerializer.Create(Options.Settings); + serializer.Serialize(writer, source, objectType); + } + /// /// Deserializes the specified into an object of . /// diff --git a/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs b/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs index b5580be00..31f5538e4 100644 --- a/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs +++ b/src/Cuemon.Xml/Serialization/Formatters/XmlFormatterOptions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Cuemon.Diagnostics; using Cuemon.Xml.Serialization.Converters; @@ -9,6 +10,20 @@ namespace Cuemon.Xml.Serialization.Formatters /// public class XmlFormatterOptions { + static XmlFormatterOptions() + { + DefaultConverters = list => + { + Decorator.Enclose(list) + .AddExceptionDescriptorConverter() + .AddEnumerableConverter() + .AddUriConverter() + .AddDateTimeConverter() + .AddTimeSpanConverter() + .AddStringConverter(); + }; + } + /// /// Initializes a new instance of the class. /// @@ -46,20 +61,17 @@ public XmlFormatterOptions() IncludeExceptionDescriptorFailure = true; IncludeExceptionDescriptorEvidence = true; IncludeExceptionStackTrace = false; - XmlSerializerOptions.DefaultConverters += list => - { - Decorator.Enclose(list) - .AddExceptionDescriptorConverter() - .AddExceptionConverter(() => IncludeExceptionStackTrace) - .AddEnumerableConverter() - .AddUriConverter() - .AddDateTimeConverter() - .AddTimeSpanConverter() - .AddStringConverter(); - }; Settings = new XmlSerializerOptions(); + Decorator.Enclose(Settings.Converters).AddExceptionConverter(() => IncludeExceptionStackTrace); + DefaultConverters?.Invoke(Settings.Converters); } + /// + /// Gets or sets a delegate that is invoked when is initialized and propagates registered implementations. + /// + /// The delegate which propagates registered implementations when is initialized. + public static Action> DefaultConverters { get; set; } + /// /// Gets or sets a value indicating whether the stack of an is included in the converter that handles exceptions. /// diff --git a/src/Cuemon.Xml/Serialization/XmlSerializer.cs b/src/Cuemon.Xml/Serialization/XmlSerializer.cs index 008c93c05..aa5b8491c 100644 --- a/src/Cuemon.Xml/Serialization/XmlSerializer.cs +++ b/src/Cuemon.Xml/Serialization/XmlSerializer.cs @@ -62,7 +62,7 @@ public Stream Serialize(object value, Type objectType) /// /// cannot be null. /// - public void Serialize(XmlWriter writer, object value, Type objectType) + internal void Serialize(XmlWriter writer, object value, Type objectType) { Validator.ThrowIfNull(writer, nameof(writer)); GetWriterConverter(objectType).WriteXml(writer, value); diff --git a/src/Cuemon.Xml/Serialization/XmlSerializerOptions.cs b/src/Cuemon.Xml/Serialization/XmlSerializerOptions.cs index 9c432a415..4fc56d638 100644 --- a/src/Cuemon.Xml/Serialization/XmlSerializerOptions.cs +++ b/src/Cuemon.Xml/Serialization/XmlSerializerOptions.cs @@ -43,15 +43,8 @@ public XmlSerializerOptions() Writer = new XmlWriterSettings() { IndentChars = Alphanumeric.Tab }; Reader = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }; Converters = new List(); - DefaultConverters?.Invoke(Converters); } - - /// - /// Gets or sets a delegate that is invoked when is initialized and propagates registered implementations. - /// - /// The delegate which propagates registered implementations when is initialized. - public static Action> DefaultConverters { get; set; } - + /// /// Gets or sets a collection that will be used during serialization. /// From 4b84d3ce1a338827dc8b741c83cf37934301b36d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 28 Sep 2020 21:22:48 +0200 Subject: [PATCH 226/385] Changed signature to match the one in the JSON variant (eg. focus on XmlFormatter and XmlFormatterOptions incl. DefaultConverters). --- .../Serialization/Formatters/XmlFormatter.cs | 15 ++++++++++++++- src/Cuemon.Xml/Serialization/XmlSerializer.cs | 11 +---------- .../Serialization/XmlSerializerOptions.cs | 3 +-- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Cuemon.Xml/Serialization/Formatters/XmlFormatter.cs b/src/Cuemon.Xml/Serialization/Formatters/XmlFormatter.cs index 78fe46743..7d7b35adc 100644 --- a/src/Cuemon.Xml/Serialization/Formatters/XmlFormatter.cs +++ b/src/Cuemon.Xml/Serialization/Formatters/XmlFormatter.cs @@ -51,6 +51,10 @@ public XmlFormatter(XmlFormatterOptions options) /// The object to serialize to XML format. /// The type of the object to serialize. /// A stream of the serialized . + /// + /// cannot be null -or- + /// cannot be null. + /// public override Stream Serialize(object source, Type objectType) { Validator.ThrowIfNull(source, nameof(source)); @@ -65,9 +69,14 @@ public override Stream Serialize(object source, Type objectType) /// The writer used in the serialization process. /// The object to serialize to XML format. /// The type of the object to serialize. - /// A stream of the serialized . + /// + /// cannot be null -or- + /// cannot be null -or- + /// cannot be null. + /// public void SerializeToWriter(XmlWriter writer, object source, Type objectType) { + Validator.ThrowIfNull(writer, nameof(writer)); Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(objectType, nameof(objectType)); var serializer = XmlSerializer.Create(Options.Settings); @@ -80,6 +89,10 @@ public void SerializeToWriter(XmlWriter writer, object source, Type objectType) /// The stream from which to deserialize the object graph. /// The type of the deserialized object. /// An object of . + /// + /// cannot be null -or- + /// cannot be null. + /// public override object Deserialize(Stream value, Type objectType) { Validator.ThrowIfNull(value, nameof(value)); diff --git a/src/Cuemon.Xml/Serialization/XmlSerializer.cs b/src/Cuemon.Xml/Serialization/XmlSerializer.cs index aa5b8491c..dab23296b 100644 --- a/src/Cuemon.Xml/Serialization/XmlSerializer.cs +++ b/src/Cuemon.Xml/Serialization/XmlSerializer.cs @@ -53,18 +53,9 @@ public Stream Serialize(object value, Type objectType) }); } - /// - /// Serializes the specified into an XML format. - /// - /// The writer used in the serialization process. - /// The object to serialize to XML format. - /// The type of the object to serialize. - /// - /// cannot be null. - /// internal void Serialize(XmlWriter writer, object value, Type objectType) { - Validator.ThrowIfNull(writer, nameof(writer)); + GetWriterConverter(objectType).WriteXml(writer, value); } diff --git a/src/Cuemon.Xml/Serialization/XmlSerializerOptions.cs b/src/Cuemon.Xml/Serialization/XmlSerializerOptions.cs index 4fc56d638..ef00a2eac 100644 --- a/src/Cuemon.Xml/Serialization/XmlSerializerOptions.cs +++ b/src/Cuemon.Xml/Serialization/XmlSerializerOptions.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Xml; using Cuemon.Xml.Serialization.Converters; From 7855413b46ffadb8831d66876308351031f99392 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 28 Sep 2020 22:15:42 +0200 Subject: [PATCH 227/385] Bootstrap default converters. --- .../Formatters/JsonFormatterOptions.cs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Formatters/JsonFormatterOptions.cs b/src/Cuemon.Extensions.Newtonsoft.Json/Formatters/JsonFormatterOptions.cs index 44014fe3f..2b9d7d9ad 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/Formatters/JsonFormatterOptions.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Formatters/JsonFormatterOptions.cs @@ -12,6 +12,17 @@ namespace Cuemon.Extensions.Newtonsoft.Json.Formatters ///
public class JsonFormatterOptions { + static JsonFormatterOptions() + { + DefaultConverters = list => + { + list.AddStringFlagsEnumConverter(); + list.AddStringEnumConverter(); + list.AddTimeSpanConverter(); + list.AddDataPairConverter(); + }; + } + /// /// Initializes a new instance of the class. /// @@ -61,19 +72,12 @@ public JsonFormatterOptions() DateTimeZoneHandling = DateTimeZoneHandling.Utc, ContractResolver = new CamelCasePropertyNamesContractResolver() }; - DefaultConverters += list => + Settings.Converters.AddExceptionConverter(() => IncludeExceptionStackTrace); + Settings.Converters.AddExceptionDescriptorConverter(o => { - list.AddStringFlagsEnumConverter(); - list.AddStringEnumConverter(); - list.AddExceptionConverter(() => IncludeExceptionStackTrace); - list.AddExceptionDescriptorConverter(o => - { - o.IncludeEvidence = IncludeExceptionDescriptorEvidence; - o.IncludeFailure = IncludeExceptionDescriptorFailure; - }); - list.AddTimeSpanConverter(); - list.AddDataPairConverter(); - }; + o.IncludeEvidence = IncludeExceptionDescriptorEvidence; + o.IncludeFailure = IncludeExceptionDescriptorFailure; + }); DefaultConverters?.Invoke(Settings.Converters); } From fdfb1fa739622dfc99fd6489c1318660e833b90b Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 28 Sep 2020 22:15:57 +0200 Subject: [PATCH 228/385] Removed linebreak. --- .../Bootstrapper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Bootstrapper.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Bootstrapper.cs index 27ace59ee..60d9d849d 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Bootstrapper.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Bootstrapper.cs @@ -6,7 +6,7 @@ namespace Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json internal static class Bootstrapper { private static readonly object PadLock = new object(); - private static bool _initialized = false; + private static bool _initialized; internal static void Initialize() { @@ -24,7 +24,6 @@ internal static void Initialize() }; } } - } } } From d8b4d1666a8ce91da3025d975bdd19042f89f731 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 28 Sep 2020 22:16:15 +0200 Subject: [PATCH 229/385] Fixed spelling error. --- src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs b/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs index 3bf039823..b0b098ace 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs @@ -6,4 +6,4 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.ValidatorExtensions.IfNotValidJsonDocument(Cuemon.Validator,Newtonsoft.Json.JsonReader@,System.String,System.String)")] -[assembly: SuppressMessage("Major Code Smell", "S907:\"goto\" statement should not be used", Justification = "Lecacy code ;-)", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.JsonReaderExtensions.ToHierarchy(Newtonsoft.Json.JsonReader)~Cuemon.IHierarchy{Cuemon.DataPair}")] \ No newline at end of file +[assembly: SuppressMessage("Major Code Smell", "S907:\"goto\" statement should not be used", Justification = "Legacy code ;-)", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.JsonReaderExtensions.ToHierarchy(Newtonsoft.Json.JsonReader)~Cuemon.IHierarchy{Cuemon.DataPair}")] \ No newline at end of file From 9f1e44fdff169258765fb20e29b60afe3bd2e7f8 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 28 Sep 2020 22:16:37 +0200 Subject: [PATCH 230/385] Renamed "parser" to "extractor". --- .../JDataResultExtensions.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/JDataResultExtensions.cs b/src/Cuemon.Extensions.Newtonsoft.Json/JDataResultExtensions.cs index 7f4d2e487..fdb1d775c 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/JDataResultExtensions.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/JDataResultExtensions.cs @@ -10,16 +10,16 @@ namespace Cuemon.Extensions.Newtonsoft.Json public static class JDataResultExtensions { /// - /// Extracts one or more values from JSON objects using the specified and delegate. + /// Extracts one or more values from JSON objects using the specified and delegate. /// /// The sequence of to parse. /// The comma-delimited property names (JSON path) to math in a JSON document. - /// The delegate that will extract values from . - public static void ExtractObjectValues(this IEnumerable source, string propertyNames, Action> parser) + /// The delegate that will extract values from . + public static void ExtractObjectValues(this IEnumerable source, string propertyNames, Action> extractor) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNullOrWhitespace(propertyNames, nameof(propertyNames)); - Validator.ThrowIfNull(parser, nameof(parser)); + Validator.ThrowIfNull(extractor, nameof(extractor)); var names = propertyNames.Split(',').Select(s => s.Trim()).ToList(); var partial = new List(); @@ -33,23 +33,23 @@ public static void ExtractObjectValues(this IEnumerable source, str if (partial.Count == names.Count) { - parser(partial.ToDictionary(jk => jk.PropertyName, jv => jv)); + extractor(partial.ToDictionary(jk => jk.PropertyName, jv => jv)); partial.Clear(); } } } /// - /// Extracts one or more values from JSON arrays using the specified and delegate. + /// Extracts one or more values from JSON arrays using the specified and delegate. /// /// The sequence of to parse. /// The comma-delimited property names (JSON path) to math in a JSON document. - /// The delegate that will extract values from . - public static void ExtractArrayValues(this IEnumerable source, string propertyNames, Action>> parser) + /// The delegate that will extract values from . + public static void ExtractArrayValues(this IEnumerable source, string propertyNames, Action>> extractor) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNullOrWhitespace(propertyNames, nameof(propertyNames)); - Validator.ThrowIfNull(parser, nameof(parser)); + Validator.ThrowIfNull(extractor, nameof(extractor)); var names = propertyNames.Split(',').Select(s => s.Trim()).ToList(); var partial = new List(); @@ -63,7 +63,7 @@ public static void ExtractArrayValues(this IEnumerable source, stri if (partial.Count == names.Count) { - parser(partial.ToDictionary(jk => jk.PropertyName, jv => jv.Children as IEnumerable)); + extractor(partial.ToDictionary(jk => jk.PropertyName, jv => jv.Children as IEnumerable)); partial.Clear(); } } From 41858ccd33db153576caf4e85cea71ab2f9c247f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 28 Sep 2020 23:05:28 +0200 Subject: [PATCH 231/385] Fixed serialization bugs triggered when type is AggregateException. --- .../Converters/JsonConverterCollectionExtensions.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs b/src/Cuemon.Extensions.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs index dfab20d48..03707bb44 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs @@ -218,10 +218,15 @@ private static void WriteExceptionCore(JsonWriter writer, Exception exception, b private static void WriteInnerExceptions(JsonWriter writer, Exception exception, bool includeStackTrace) { - var aggregated = exception as AggregateException; var innerExceptions = new List(); - if (aggregated != null) { innerExceptions.AddRange(aggregated.Flatten().InnerExceptions); } - if (exception.InnerException != null) { innerExceptions.Add(exception.InnerException); } + if (exception is AggregateException aggregated) + { + innerExceptions.AddRange(aggregated.Flatten().InnerExceptions); + } + else + { + if (exception.InnerException != null) { innerExceptions.Add(exception.InnerException); } + } if (innerExceptions.Count > 0) { var endElementsToWrite = 0; From 18d6a975e52157dc4c605c53d7939b6186732ac9 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 29 Sep 2020 02:23:52 +0200 Subject: [PATCH 232/385] Updated package release notes. --- .../Properties/PackageReleaseNotes.txt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..a3dca2073 --- /dev/null +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt @@ -0,0 +1,21 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# Upgrade Steps +- The Cuemon.Serialization.Json namespace was removed with this version +- Any types found in the Cuemon.Serialization.Json namespace was merged into the Cuemon.Extensions.Newtonsoft.Json namespace +  +# Breaking Changes +- REMOVED JsonReaderParser class from the Cuemon.Extensions.Newtonsoft.Json namespace +- RENAMED JsonReaderResult class in the Cuemon.Extensions.Newtonsoft.Json namespace to JDataResult (including some refactoring) +- REMOVED JsonReaderResultExtensions class from the Cuemon.Extensions.Newtonsoft.Json namespace +  +# New Features +- CHANGED StringFlagsEnumConverter class in the Cuemon.Extensions.Newtonsoft.Json.Converters namespace to comply with Newtonsoft.Json.Serialization.NamingStrategy implementations +- ADDED ExceptionDescriptorExtensions class in the Cuemon.Extensions.Newtonsoft.Json.Diagnostics namespace that consist of extension methods for the ExceptionDescriptor class: ToInsightsJsonString +- EXTENDED JsonFormatterOptions class in the Cuemon.Extensions.Newtonsoft.Json namespace with several new options and a uniform way of adding default converters +- ADDED JData class in the Cuemon.Extensions.Newtonsoft.Json namespace that provides a factory based way to parse and extract values from various sources of JSON data. Compliant with RFC 7159 as it uses JsonTextReader behind the scene +- ADDED JDataResultExtensions class in the Cuemon.Extensions.Newtonsoft.Json namespace that consist of extension methods for the JDataResult class: ExtractArrayValues, ExtractObjectValues +- EXTENDED JsonSerializerSettingsExtensions class in the Cuemon.Extensions.Newtonsoft.Json namespace with one new extension method for the JsonSerializerSettings class: UseCamelCase +- ADDED ValidatorExtensions class in the Cuemon.Extensions.Newtonsoft.Json namespace that consist of extension methods for the Validator class: IfNotValidJsonDocument +  \ No newline at end of file From 34741ebc3e5fd2bc9a607158d2c19be6ae96d9be Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 29 Sep 2020 02:24:23 +0200 Subject: [PATCH 233/385] Refactored and fixed a bug or two ;-) --- .../GlobalSuppressions.cs | 3 +- .../JData.cs | 57 ++++++++----------- .../JDataResult.cs | 3 +- .../JDataResultExtensions.cs | 17 ++++++ 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs b/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs index b0b098ace..695b4cbba 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs @@ -6,4 +6,5 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.ValidatorExtensions.IfNotValidJsonDocument(Cuemon.Validator,Newtonsoft.Json.JsonReader@,System.String,System.String)")] -[assembly: SuppressMessage("Major Code Smell", "S907:\"goto\" statement should not be used", Justification = "Legacy code ;-)", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.JsonReaderExtensions.ToHierarchy(Newtonsoft.Json.JsonReader)~Cuemon.IHierarchy{Cuemon.DataPair}")] \ No newline at end of file +[assembly: SuppressMessage("Major Code Smell", "S907:\"goto\" statement should not be used", Justification = "Legacy code ;-)", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.JsonReaderExtensions.ToHierarchy(Newtonsoft.Json.JsonReader)~Cuemon.IHierarchy{Cuemon.DataPair}")] +[assembly: SuppressMessage("Critical Code Smell", "S3776:Cognitive Complexity of methods should not be too high", Justification = "Legacy code ;-)", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.JsonReaderExtensions.ToHierarchy(Newtonsoft.Json.JsonReader)~Cuemon.IHierarchy{Cuemon.DataPair}")] diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/JData.cs b/src/Cuemon.Extensions.Newtonsoft.Json/JData.cs index a52a99c6f..2329036fe 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/JData.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/JData.cs @@ -8,7 +8,7 @@ namespace Cuemon.Extensions.Newtonsoft.Json { /// - /// Provides a factory based way to parse and extract values from various sources of JSON data. + /// Provides a factory based way to parse and extract values from various sources of JSON data. Compliant with RFC 7159 as it uses behind the scene. /// public class JData { @@ -24,7 +24,6 @@ public static IEnumerable ReadAll(Stream json, Action(); while (reader.Read()) { - if (reader.Value == null) { continue; } var jr = new JDataResult(); switch (reader.TokenType) { @@ -78,16 +76,20 @@ internal JData(JsonReader reader) reader.Read(); break; } + if (reader.TokenType == JsonToken.StartArray) { - jr.Children = FillArray(reader, jr); + return FillArrayHierarchy(reader, jr); } - else + + if (reader.TokenType == JsonToken.StartObject) { - jr.Value = reader.Value; + return FillObjectHierarchy(reader, jr); } + + jr.Value = reader.Value; jr.Path = reader.Path.RemoveBrackets(); - jr.Type = reader.ValueType ?? typeof(Array); + jr.Type = reader.ValueType; result.Add(jr); } return result; @@ -96,33 +98,23 @@ internal JData(JsonReader reader) private Lazy> Result { get; } - private List FillArray(JsonReader reader, JDataResult parent) + + private List FillArrayHierarchy(JsonReader reader, JDataResult parent) { - var result = new List(); - while (reader.Read()) - { - var jr = new JDataResult { Parent = parent }; - if (reader.TokenType == JsonToken.EndArray) { break; } - if (reader.TokenType == JsonToken.StartObject) - { - jr.Children = FillObjectArray(reader, jr); - } - else - { - jr.Value = reader.Value; - } - jr.Path = reader.Path.RemoveBrackets(); - jr.Type = reader.ValueType ?? typeof(Array); - result.Add(jr); - } - return result; + return FillHierarchy(reader, parent, r => r.TokenType == JsonToken.EndArray); + } + + private List FillObjectHierarchy(JsonReader reader, JDataResult parent) + { + return FillHierarchy(reader, parent, r => r.TokenType == JsonToken.EndObject); } - private List FillObjectArray(JsonReader reader, JDataResult parent) + private List FillHierarchy(JsonReader reader, JDataResult parent, Func skipWhenTrue) { var result = new List(); while (reader.Read()) { + if (skipWhenTrue(reader)) { break; } var jr = new JDataResult { Parent = parent }; switch (reader.TokenType) { @@ -131,23 +123,24 @@ private List FillObjectArray(JsonReader reader, JDataResult parent) reader.Read(); break; } - if (reader.TokenType == JsonToken.EndObject) { break; } if (reader.TokenType == JsonToken.StartArray) { - jr.Children = FillArray(reader, jr); + jr.Children = FillArrayHierarchy(reader, jr); + } + else if (reader.TokenType == JsonToken.StartObject) + { + jr.Children = FillObjectHierarchy(reader, jr); } else { jr.Value = reader.Value; } jr.Path = reader.Path.RemoveBrackets(); - jr.Type = reader.ValueType ?? typeof(Array); + jr.Type = reader.ValueType; result.Add(jr); } return result; } - - } internal static class RegexExtensions diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/JDataResult.cs b/src/Cuemon.Extensions.Newtonsoft.Json/JDataResult.cs index 8b4dd0f5f..5f087f94f 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/JDataResult.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/JDataResult.cs @@ -50,7 +50,8 @@ public class JDataResult /// A that represents this instance. public override string ToString() { - return FormattableString.Invariant($"{Path} ({Type.Name.ToLowerInvariant()}), Children: {Children.Count}"); + var path = string.IsNullOrWhiteSpace(Path) ? "" : $"{Path}, "; + return FormattableString.Invariant($"{path}Children: {Children.Count}"); } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/JDataResultExtensions.cs b/src/Cuemon.Extensions.Newtonsoft.Json/JDataResultExtensions.cs index fdb1d775c..0cb4cb0e1 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/JDataResultExtensions.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/JDataResultExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Cuemon.Collections.Generic; namespace Cuemon.Extensions.Newtonsoft.Json { @@ -9,6 +10,22 @@ namespace Cuemon.Extensions.Newtonsoft.Json ///
public static class JDataResultExtensions { + /// + /// Flattens the entirety of the JSON hierarchical into an sequence. + /// + /// The to extend. + /// An sequence of objects. + public static IEnumerable Flatten(this IEnumerable source) + { + Validator.ThrowIfNull(source, nameof(source)); + return FlattenCore(source); + } + + private static IEnumerable FlattenCore(IEnumerable source) + { + return source.SelectMany(s => s.Children.Any() ? Arguments.Yield(s).Concat(Flatten(s.Children)) : Arguments.Yield(s)); + } + /// /// Extracts one or more values from JSON objects using the specified and delegate. /// From 021d97a0c8db7409c26979b5ae4f140ea0854285 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 29 Sep 2020 02:24:38 +0200 Subject: [PATCH 234/385] Unit test for both JSON and XML formatters. --- ...on.Extensions.Newtonsoft.Json.Tests.csproj | 13 ++ .../Formatters/JsonFormatterOptionsTest.cs | 33 +++++ .../Formatters/JsonFormatterTest.cs | 56 +++++++++ .../JDataTest.cs | 113 ++++++++++++++++++ .../Formatters/XmlFormatterOptionsTest.cs | 33 +++++ 5 files changed, 248 insertions(+) create mode 100644 test/Cuemon.Extensions.Newtonsoft.Json.Tests/Cuemon.Extensions.Newtonsoft.Json.Tests.csproj create mode 100644 test/Cuemon.Extensions.Newtonsoft.Json.Tests/Formatters/JsonFormatterOptionsTest.cs create mode 100644 test/Cuemon.Extensions.Newtonsoft.Json.Tests/Formatters/JsonFormatterTest.cs create mode 100644 test/Cuemon.Extensions.Newtonsoft.Json.Tests/JDataTest.cs create mode 100644 test/Cuemon.Xml.Tests/Serialization/Formatters/XmlFormatterOptionsTest.cs diff --git a/test/Cuemon.Extensions.Newtonsoft.Json.Tests/Cuemon.Extensions.Newtonsoft.Json.Tests.csproj b/test/Cuemon.Extensions.Newtonsoft.Json.Tests/Cuemon.Extensions.Newtonsoft.Json.Tests.csproj new file mode 100644 index 000000000..febd3b6cf --- /dev/null +++ b/test/Cuemon.Extensions.Newtonsoft.Json.Tests/Cuemon.Extensions.Newtonsoft.Json.Tests.csproj @@ -0,0 +1,13 @@ + + + + Cuemon.Extensions.Newtonsoft.Json + + + + + + + + + \ No newline at end of file diff --git a/test/Cuemon.Extensions.Newtonsoft.Json.Tests/Formatters/JsonFormatterOptionsTest.cs b/test/Cuemon.Extensions.Newtonsoft.Json.Tests/Formatters/JsonFormatterOptionsTest.cs new file mode 100644 index 000000000..13878ef2c --- /dev/null +++ b/test/Cuemon.Extensions.Newtonsoft.Json.Tests/Formatters/JsonFormatterOptionsTest.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using Cuemon.Extensions.Xunit; +using Newtonsoft.Json; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Extensions.Newtonsoft.Json.Formatters +{ + public class JsonFormatterOptionsTest : Test + { + public JsonFormatterOptionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void DefaultConverters_ShouldHaveSameAmountOfDefaultConverters() + { + var defaultConverters = new List(); + JsonFormatterOptions.DefaultConverters(defaultConverters); + + var x = new JsonFormatterOptions(); + var y = new JsonFormatterOptions(); + var bootstrapInvocationList = JsonFormatterOptions.DefaultConverters.GetInvocationList().Length; + + Assert.Equal(4, defaultConverters.Count); + Assert.Equal(1, bootstrapInvocationList); + Assert.Equal(2, x.Settings.Converters.Count - defaultConverters.Count); + Assert.Equal(2, y.Settings.Converters.Count - defaultConverters.Count); + + Assert.Equal(x.Settings.Converters.Count, y.Settings.Converters.Count); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Newtonsoft.Json.Tests/Formatters/JsonFormatterTest.cs b/test/Cuemon.Extensions.Newtonsoft.Json.Tests/Formatters/JsonFormatterTest.cs new file mode 100644 index 000000000..7ad08f5ee --- /dev/null +++ b/test/Cuemon.Extensions.Newtonsoft.Json.Tests/Formatters/JsonFormatterTest.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading; +using Cuemon.Extensions.IO; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Extensions.Newtonsoft.Json.Formatters +{ + public class JsonFormatterTest : Test + { + public JsonFormatterTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Serialize_ShouldSerializeUsingExceptionConverter() + { + try + { + throw new OutOfMemoryException("First", new AggregateException(new AccessViolationException("I1"), new AbandonedMutexException("I2"), new ArithmeticException("I3"))); + } + catch (Exception e) + { + e.Data.Add("Cuemon", "XmlFormatterTest"); + var f = new JsonFormatter(o => + { + o.IncludeExceptionStackTrace = true; + }); + var r = f.Serialize(e); + var x = new StreamReader(r).ReadAllLines().ToList(); + Assert.Contains(e.Data.Keys.Cast(), s => s.Equals("Cuemon")); + Assert.Contains(e.Data.Values.Cast(), s => s.Equals("XmlFormatterTest")); + Assert.Equal("{", x[0]); + Assert.Contains("\"Type\": \"System.OutOfMemoryException\"", x[1]); + Assert.Contains("\"Source\": \"Cuemon.Extensions.Newtonsoft.Json.Tests\"", x[2]); + Assert.Contains("\"Message\": \"First\"", x[3]); + Assert.Contains("\"Stack\": [", x[4]); + Assert.Contains("at Cuemon.Extensions.Newtonsoft.Json.Formatters.JsonFormatterTest", x[5]); + Assert.Contains("\"Data\": {", x[7]); + Assert.Contains("\"Cuemon\": \"XmlFormatterTest\"", x[8]); + Assert.Contains("},", x[9]); + Assert.Contains("\"Inner\": {", x[10]); + Assert.Contains("\"Type\": \"System.AggregateException\",", x[11]); + Assert.Contains("\"Type\": \"System.AccessViolationException\"", x[14]); + Assert.Contains("\"Type\": \"System.Threading.AbandonedMutexException\"", x[17]); + Assert.Contains("\"Type\": \"System.ArithmeticException\"", x[21]); + + TestOutput.WriteLine(r.ToEncodedString()); + r.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Newtonsoft.Json.Tests/JDataTest.cs b/test/Cuemon.Extensions.Newtonsoft.Json.Tests/JDataTest.cs new file mode 100644 index 000000000..14404efb3 --- /dev/null +++ b/test/Cuemon.Extensions.Newtonsoft.Json.Tests/JDataTest.cs @@ -0,0 +1,113 @@ +using System; +using System.Linq; +using System.Threading; +using Cuemon.Extensions.IO; +using Cuemon.Extensions.Newtonsoft.Json.Formatters; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Extensions.Newtonsoft.Json +{ + public class JDataTest : Test + { + public JDataTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void ReadAll_PrimitiveValuesShouldBeCompliantWithRfc7159() + { + object[] primitives = { "null", 1, 2, 3, 4, 5, 6, 7, 8, 9, "\"cuemon\"", false, true, 100.4 }; + + foreach (var p in primitives) + { + var f = new JsonFormatter(); + var r = f.Serialize(p); + + var x0 = JData.ReadAll(r, o => o.LeaveOpen = true); + + TestOutput.WriteLine(DelimitedString.Create(x0, o => o.Delimiter = Environment.NewLine)); + TestOutput.WriteLine(r.ToEncodedString()); + } + } + + [Fact] + public void ReadAll_ShouldReadSimpleObject() + { + var e = new ArgumentException("The amazing message of this exception.", "fakeArg"); + var f = new JsonFormatter(); + var r = f.Serialize(e); + var x0 = JData.ReadAll(r, o => o.LeaveOpen = true); + + TestOutput.WriteLine(DelimitedString.Create(x0, o => o.Delimiter = Environment.NewLine)); + + TestOutput.WriteLine(r.ToEncodedString()); + } + + [Fact] + public void ReadAll_ShouldReadSimpleArray() + { + var a = Generate.RangeOf(50, i => i); + var f = new JsonFormatter(); + var r = f.Serialize(a); + var x0 = JData.ReadAll(r, o => o.LeaveOpen = true).ToList(); + + for (var i = 0; i < 50; i++) + { + Assert.Equal(Convert.ChangeType(i, x0[i].Type), x0[i].Value); // should be int32 - but Newtonsoft resolves it as int64 + } + + TestOutput.WriteLine(r.ToEncodedString()); + } + + [Fact] + public void ReadAll_ShouldHaveOuterAndNestedExceptionsBothHierarchyAndFlattened() + { + var e = new OutOfMemoryException("First", new AggregateException(new AccessViolationException("I1"), new AbandonedMutexException("I2"), new ArithmeticException("I3"))); + var f = new JsonFormatter(); + var r = f.Serialize(e); + var x0 = JData.ReadAll(r, o => o.LeaveOpen = true); + var x1 = x0.Last(r => r.Children.Any()).Children; + var x2 = x1.Last(r => r.Children.Any()).Children; + var x3 = x2.Last(r => r.Children.Any()).Children; + var x4 = x3.Last(r => r.Children.Any()).Children; + var xFlat = x0.Flatten().ToList(); + + Assert.Equal(3, x0.Count()); + Assert.Equal(3, x1.Count); + Assert.Equal(3, x2.Count); + Assert.Equal(4, x3.Count); + Assert.Equal(2, x4.Count); + + Assert.Equal("System.OutOfMemoryException", x0.Single(result => result.PropertyName == "Type").Value); + Assert.Equal("First", x0.Single(result => result.PropertyName == "Message").Value); + Assert.Equal("System.AggregateException", x1.Single(result => result.PropertyName == "Type").Value); + Assert.Equal("One or more errors occurred. (I1) (I2) (I3)", x1.Single(result => result.PropertyName == "Message").Value); + Assert.Equal("System.AccessViolationException", x2.Single(result => result.PropertyName == "Type").Value); + Assert.Equal("I1", x2.Single(result => result.PropertyName == "Message").Value); + Assert.Equal("System.Threading.AbandonedMutexException", x3.Single(result => result.PropertyName == "Type").Value); + Assert.Equal("I2", x3.Single(result => result.PropertyName == "Message").Value); + var x3jdr = x3.Single(result => result.PropertyName == "MutexIndex"); + Assert.Equal(Convert.ChangeType(-1, x3jdr.Type), x3jdr.Value); // should be int32 - but Newtonsoft resolves it as int64 + Assert.Equal("System.ArithmeticException", x4.Single(result => result.PropertyName == "Type").Value); + Assert.Equal("I3", x4.Single(result => result.PropertyName == "Message").Value); + + Assert.Equal("System.OutOfMemoryException", xFlat[0].Value); + Assert.Equal("First", xFlat[1].Value); + Assert.Equal("System.AggregateException", xFlat[3].Value); + Assert.Equal("One or more errors occurred. (I1) (I2) (I3)", xFlat[4].Value); + Assert.Equal("System.AccessViolationException", xFlat[6].Value); + Assert.Equal("I1", xFlat[7].Value); + Assert.Equal("System.Threading.AbandonedMutexException", xFlat[9].Value); + Assert.Equal("I2", xFlat[10].Value); + Assert.Equal(Convert.ChangeType(-1, xFlat[11].Type), xFlat[11].Value); // should be int32 - but Newtonsoft resolves it as int64 + Assert.Equal("System.ArithmeticException", xFlat[13].Value); + Assert.Equal("I3", xFlat[14].Value); + + TestOutput.WriteLine(DelimitedString.Create(xFlat, o => o.Delimiter = Environment.NewLine)); + TestOutput.WriteLine(""); + TestOutput.WriteLine(r.ToEncodedString()); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Xml.Tests/Serialization/Formatters/XmlFormatterOptionsTest.cs b/test/Cuemon.Xml.Tests/Serialization/Formatters/XmlFormatterOptionsTest.cs new file mode 100644 index 000000000..20a62c88e --- /dev/null +++ b/test/Cuemon.Xml.Tests/Serialization/Formatters/XmlFormatterOptionsTest.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using Cuemon.Extensions.Xunit; +using Cuemon.Xml.Serialization.Converters; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Xml.Serialization.Formatters +{ + public class XmlFormatterOptionsTest : Test + { + public XmlFormatterOptionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void DefaultConverters_ShouldHaveSameAmountOfDefaultConverters() + { + var defaultConverters = new List(); + XmlFormatterOptions.DefaultConverters(defaultConverters); + + var x = new XmlFormatterOptions(); + var y = new XmlFormatterOptions(); + var bootstrapInvocationList = XmlFormatterOptions.DefaultConverters.GetInvocationList().Length; + + Assert.Equal(6, defaultConverters.Count); + Assert.Equal(1, bootstrapInvocationList); + Assert.Equal(1, x.Settings.Converters.Count - defaultConverters.Count); + Assert.Equal(1, y.Settings.Converters.Count - defaultConverters.Count); + + Assert.Equal(x.Settings.Converters.Count, y.Settings.Converters.Count); + } + } +} \ No newline at end of file From 7a3c9b17eebf4a701ba42a9d396c33317ee4c737 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 29 Sep 2020 02:46:13 +0200 Subject: [PATCH 235/385] Updated DocFx namespace descriptions and package description. --- Cuemon.sln | 21 ++++++++++------ ...n.Extensions.Newtonsoft.Json.Converters.md | 21 +++++++++++++++- ....Extensions.Newtonsoft.Json.Diagnostics.md | 21 +++++++++++++++- ...n.Extensions.Newtonsoft.Json.Formatters.md | 15 ++++++++++- .../Cuemon.Extensions.Newtonsoft.Json.md | 25 ++++++++++++++++++- .../Cuemon.Extensions.Newtonsoft.Json.csproj | 4 +-- 6 files changed, 94 insertions(+), 13 deletions(-) diff --git a/Cuemon.sln b/Cuemon.sln index 0a3748e79..975d9193b 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -119,9 +119,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Hosting.X EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Runtime.Caching.Tests", "test\Cuemon.Runtime.Caching.Tests\Cuemon.Runtime.Caching.Tests.csproj", "{581174AB-62AA-4A04-85DE-4F9E307C9712}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Runtime.Caching", "src\Cuemon.Extensions.Runtime.Caching\Cuemon.Extensions.Runtime.Caching.csproj", "{487E6256-B4CA-4E3D-935D-F775B98DCDF2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Runtime.Caching", "src\Cuemon.Extensions.Runtime.Caching\Cuemon.Extensions.Runtime.Caching.csproj", "{1F0BDF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Runtime.Caching.Tests", "test\Cuemon.Extensions.Runtime.Caching.Tests\Cuemon.Extensions.Runtime.Caching.Tests.csproj", "{0F614FD1-BC7C-4F7F-9847-D3675614576C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Runtime.Caching.Tests", "test\Cuemon.Extensions.Runtime.Caching.Tests\Cuemon.Extensions.Runtime.Caching.Tests.csproj", "{0F614FD1-BC7C-4F7F-9847-D3675614576C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Newtonsoft.Json.Tests", "test\Cuemon.Extensions.Newtonsoft.Json.Tests\Cuemon.Extensions.Newtonsoft.Json.Tests.csproj", "{8A3E26BD-A3C4-4684-909B-1ABDFDB4108D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -353,14 +355,18 @@ Global {581174AB-62AA-4A04-85DE-4F9E307C9712}.Debug|Any CPU.Build.0 = Debug|Any CPU {581174AB-62AA-4A04-85DE-4F9E307C9712}.Release|Any CPU.ActiveCfg = Release|Any CPU {581174AB-62AA-4A04-85DE-4F9E307C9712}.Release|Any CPU.Build.0 = Release|Any CPU - {487E6256-B4CA-4E3D-935D-F775B98DCDF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {487E6256-B4CA-4E3D-935D-F775B98DCDF2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {487E6256-B4CA-4E3D-935D-F775B98DCDF2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {487E6256-B4CA-4E3D-935D-F775B98DCDF2}.Release|Any CPU.Build.0 = Release|Any CPU + {1F0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F0BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.Build.0 = Release|Any CPU {0F614FD1-BC7C-4F7F-9847-D3675614576C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0F614FD1-BC7C-4F7F-9847-D3675614576C}.Debug|Any CPU.Build.0 = Debug|Any CPU {0F614FD1-BC7C-4F7F-9847-D3675614576C}.Release|Any CPU.ActiveCfg = Release|Any CPU {0F614FD1-BC7C-4F7F-9847-D3675614576C}.Release|Any CPU.Build.0 = Release|Any CPU + {8A3E26BD-A3C4-4684-909B-1ABDFDB4108D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A3E26BD-A3C4-4684-909B-1ABDFDB4108D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A3E26BD-A3C4-4684-909B-1ABDFDB4108D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A3E26BD-A3C4-4684-909B-1ABDFDB4108D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -422,8 +428,9 @@ Global {2108E7E7-F002-481C-B17F-918E76D98378} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {86B43822-0733-416E-8DA2-666C5657974F} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {581174AB-62AA-4A04-85DE-4F9E307C9712} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} - {487E6256-B4CA-4E3D-935D-F775B98DCDF2} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} + {1F0BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} {0F614FD1-BC7C-4F7F-9847-D3675614576C} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} + {8A3E26BD-A3C4-4684-909B-1ABDFDB4108D} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2A848386-B682-4F6D-8254-B5F6247C3054} diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md index 6a0c49da5..eb49a6ebb 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.Newtonsoft.Json.Converters summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Newtonsoft.Json.Converters namespace contains both types and extension methods that complements the Newtonsoft.Json.Converters namespace. + +Availability: NET Standard 2.0 + +Complements: [Newtonsoft.Json.Converters namespace](https://www.newtonsoft.com/json/help/html/N_Newtonsoft_Json_Converters.htm) 🔗 + +Github branches: 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Newtonsoft.Json/Converters)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Newtonsoft.Json/Converters)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Newtonsoft.Json/Converters) + +NuGet packages 📦\ +[Cuemon.Extensions.Newtonsoft.Json (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Newtonsoft.Json)\ +[Cuemon.Extensions.Newtonsoft.Json (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Newtonsoft.Json) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|JsonConverter|⬇️|`AddStringEnumConverter`, `AddStringFlagsEnumConverter`, `AddExceptionDescriptorConverter`, `AddTimeSpanConverter`, `AddExceptionConverter`, `AddDataPairConverter`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md index 1d8d95b1f..f60dee302 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.Newtonsoft.Json.Diagnostics summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Newtonsoft.Json.Diagnostics namespace contains extension methods that complements the Cuemon.Diagnostics namespace. + +Availability: NET Standard 2.0 + +Complements: [Cuemon.Diagnostics namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.html) 🔗 + +Github branches: 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Newtonsoft.Json/Diagnostics)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Newtonsoft.Json/Diagnostics)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Newtonsoft.Json/Diagnostics) + +NuGet packages 📦\ +[Cuemon.Extensions.Newtonsoft.Json (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Newtonsoft.Json)\ +[Cuemon.Extensions.Newtonsoft.Json (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Newtonsoft.Json) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|ExceptionDescriptor|⬇️|`ToInsightsJsonString`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md index fbc0de896..76be0e60e 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md @@ -2,4 +2,17 @@ uid: Cuemon.Extensions.Newtonsoft.Json.Formatters summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Newtonsoft.Json.Formatters namespace contains types that are used to serialize and deserialize objects into and from JSON format using a generic signature. + +Availability: NET Standard 2.0 + +Complements: [Newtonsoft.Json.Serialization namespace](https://www.newtonsoft.com/json/help/html/N_Newtonsoft_Json_Serialization.htm) 🔗 + +Github branches: 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Newtonsoft.Json/Formatters)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Newtonsoft.Json/Formatters)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Newtonsoft.Json/Formatters) + +NuGet packages 📦\ +[Cuemon.Extensions.Newtonsoft.Json (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Newtonsoft.Json)\ +[Cuemon.Extensions.Newtonsoft.Json (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Newtonsoft.Json) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md index 83b0fce05..7968854b1 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md @@ -2,4 +2,27 @@ uid: Cuemon.Extensions.Newtonsoft.Json summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Newtonsoft.Json namespace contains both types and extension methods that complements the Newtonsoft.Json namespace by adding new ways of working with JSON; both in terms of serialization and parsing. + +Availability: NET Standard 2.0 + +Complements: [Newtonsoft.Json namespace](https://www.newtonsoft.com/json/help/html/N_Newtonsoft_Json.htm) 🔗 + +Github branches: 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Newtonsoft.Json)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Newtonsoft.Json)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Newtonsoft.Json) + +NuGet packages 📦\ +[Cuemon.Extensions.Newtonsoft.Json (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Newtonsoft.Json)\ +[Cuemon.Extensions.Newtonsoft.Json (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Newtonsoft.Json) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|JDataResult|⬇️|`Flatten`, `ExtractArrayValues`, `ExtractObjectValues`| +|JsonReader|⬇️|`ToHierarchy`| +|JsonSerializerSettings|⬇️|`ApplyToDefaultSettings`, `UseCamelCase`| +|JsonWriter|⬇️|`WriteObject`, `WritePropertyName`| +|Validator|⬇️|`IfNotValidJsonDocument`| \ No newline at end of file diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj index 9d07231d5..4915b03d9 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj @@ -8,8 +8,8 @@ Cuemon.Extensions.Newtonsoft.Json Cuemon.Extensions.Newtonsoft.Json - The Cuemon.Extensions.Newtonsoft.Json namespace contains extension methods and features that is related to the Newtonsoft.Json namespace. - extension-methods extensions jdata jdata-result json-converter json-formatter + The Cuemon.Extensions.Newtonsoft.Json namespace contains both types and extension methods that complements the Newtonsoft.Json namespace by adding new ways of working with JSON; both in terms of serialization and parsing. + extension-methods extensions jdata jdata-result json-converter json-formatter dynamic-contract-resolver dynamic-json-converter From ccfd510dabb07ebca355cbe12b8110ee1140cb5d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 30 Sep 2020 23:22:51 +0200 Subject: [PATCH 236/385] Aligned argument name and added validation for ArgumentNullException. --- .../Http/HttpMethodExtensions.cs | 6 +++++- src/Cuemon.Net/Http/HttpMethodConverter.cs | 17 +++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Cuemon.Extensions.Net/Http/HttpMethodExtensions.cs b/src/Cuemon.Extensions.Net/Http/HttpMethodExtensions.cs index c4d7270d7..223be3203 100644 --- a/src/Cuemon.Extensions.Net/Http/HttpMethodExtensions.cs +++ b/src/Cuemon.Extensions.Net/Http/HttpMethodExtensions.cs @@ -1,4 +1,5 @@ -using System.Net.Http; +using System; +using System.Net.Http; using Cuemon.Net.Http; namespace Cuemon.Extensions.Net.Http @@ -13,6 +14,9 @@ public static class HttpMethodExtensions ///
/// The to be converted. /// A representation of the specified . + /// + /// cannot be null. + /// public static HttpMethods ToHttpMethod(this HttpMethod method) { return HttpMethodConverter.ToHttpMethod(method); diff --git a/src/Cuemon.Net/Http/HttpMethodConverter.cs b/src/Cuemon.Net/Http/HttpMethodConverter.cs index 3659d99e5..e81c63a30 100644 --- a/src/Cuemon.Net/Http/HttpMethodConverter.cs +++ b/src/Cuemon.Net/Http/HttpMethodConverter.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Net.Http; using Cuemon.Collections.Generic; using Cuemon.Text; @@ -23,13 +24,17 @@ private static IDictionary InitStringToHttpMethodLookupTabl } /// - /// Converts the specified to its equivalent representation. + /// Converts the specified to its equivalent representation. /// - /// The to be converted. - /// A representation of the specified . - public static HttpMethods ToHttpMethod(HttpMethod source) + /// The to be converted. + /// A representation of the specified . + /// + /// cannot be null. + /// + public static HttpMethods ToHttpMethod(HttpMethod method) { - if (!StringToHttpMethodLookupTable.TryGetValue(source.Method, out var result)) + Validator.ThrowIfNull(method, nameof(method)); + if (!StringToHttpMethodLookupTable.TryGetValue(method.Method, out var result)) { result = HttpMethods.Get; } From 9fa258e0644d2c9892ecc8f806ab458322dd7c6d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 30 Sep 2020 23:23:37 +0200 Subject: [PATCH 237/385] Added validation on location (ArgumentNullException). --- .../Http/SlimHttpClientFactory.cs | 2 - .../Http/UriExtensions.cs | 76 +++++++++++++++---- src/Cuemon.Net/Http/HttpManager.cs | 4 + 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/src/Cuemon.Extensions.Net/Http/SlimHttpClientFactory.cs b/src/Cuemon.Extensions.Net/Http/SlimHttpClientFactory.cs index c3640b278..13389610d 100644 --- a/src/Cuemon.Extensions.Net/Http/SlimHttpClientFactory.cs +++ b/src/Cuemon.Extensions.Net/Http/SlimHttpClientFactory.cs @@ -9,8 +9,6 @@ namespace Cuemon.Extensions.Net.Http { /// /// Provides a simple and lightweight implementation of the interface. - /// Implements the - /// Implements the /// /// /// diff --git a/src/Cuemon.Extensions.Net/Http/UriExtensions.cs b/src/Cuemon.Extensions.Net/Http/UriExtensions.cs index e2268726c..91170f91b 100644 --- a/src/Cuemon.Extensions.Net/Http/UriExtensions.cs +++ b/src/Cuemon.Extensions.Net/Http/UriExtensions.cs @@ -39,9 +39,12 @@ public static IHttpClientFactory DefaultHttpClientFactory /// /// Send a DELETE request to the specified Uri as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpDeleteAsync(this Uri location, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpDeleteAsync(location, ct).ConfigureAwait(false); @@ -50,9 +53,12 @@ public static async Task HttpDeleteAsync(this Uri location, /// /// Send a GET request to the specified Uri as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpGetAsync(this Uri location, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpGetAsync(location, ct).ConfigureAwait(false); @@ -61,9 +67,12 @@ public static async Task HttpGetAsync(this Uri location, Ca /// /// Send a HEAD request to the specified Uri as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpHeadAsync(this Uri location, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpHeadAsync(location, ct).ConfigureAwait(false); @@ -72,9 +81,12 @@ public static async Task HttpHeadAsync(this Uri location, C /// /// Send an OPTIONS request to the specified Uri as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpOptionsAsync(this Uri location, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpOptionsAsync(location, ct).ConfigureAwait(false); @@ -83,11 +95,14 @@ public static async Task HttpOptionsAsync(this Uri location /// /// Send a POST request to the specified Uri as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The Content-Type header of the HTTP request sent to the server. /// The HTTP request content sent to the server. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpPostAsync(this Uri location, string contentType, Stream content, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpPostAsync(location, contentType, content, ct).ConfigureAwait(false); @@ -96,11 +111,14 @@ public static async Task HttpPostAsync(this Uri location, s /// /// Send a POST request to the specified Uri as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The Content-Type header of the HTTP request sent to the server. /// The HTTP request content sent to the server. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpPostAsync(this Uri location, MediaTypeHeaderValue contentType, Stream content, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpPostAsync(location, contentType, content, ct).ConfigureAwait(false); @@ -109,11 +127,14 @@ public static async Task HttpPostAsync(this Uri location, M /// /// Send a PUT request to the specified Uri as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The Content-Type header of the HTTP request sent to the server. /// The HTTP request content sent to the server. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpPutAsync(this Uri location, string contentType, Stream content, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpPutAsync(location, contentType, content, ct).ConfigureAwait(false); @@ -122,11 +143,14 @@ public static async Task HttpPutAsync(this Uri location, st /// /// Send a PUT request to the specified Uri as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The Content-Type header of the HTTP request sent to the server. /// The HTTP request content sent to the server. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpPutAsync(this Uri location, MediaTypeHeaderValue contentType, Stream content, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpPutAsync(location, contentType, content, ct).ConfigureAwait(false); @@ -135,11 +159,14 @@ public static async Task HttpPutAsync(this Uri location, Me /// /// Send a PATCH request to the specified Uri as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The Content-Type header of the HTTP request sent to the server. /// The HTTP request content sent to the server. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpPatchAsync(this Uri location, string contentType, Stream content, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpPatchAsync(location, contentType, content, ct).ConfigureAwait(false); @@ -148,11 +175,14 @@ public static async Task HttpPatchAsync(this Uri location, /// /// Send a PATCH request to the specified Uri as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The Content-Type header of the HTTP request sent to the server. /// The HTTP request content sent to the server. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpPatchAsync(this Uri location, MediaTypeHeaderValue contentType, Stream content, CancellationToken ct = default) { @@ -162,9 +192,12 @@ public static async Task HttpPatchAsync(this Uri location, /// /// Send a TRACE request to the specified Uri as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpTraceAsync(this Uri location, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpTraceAsync(location, ct).ConfigureAwait(false); @@ -173,12 +206,18 @@ public static async Task HttpTraceAsync(this Uri location, /// /// Send a request as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The HTTP method. /// The Content-Type header of the HTTP request sent to the server. /// The HTTP request content sent to the server. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null -or- + /// cannot be null -or- + /// cannot be null -or- + /// cannot be null. + /// public static async Task HttpAsync(this Uri location, HttpMethod method, string contentType, Stream content, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpAsync(method, location, contentType, content, ct).ConfigureAwait(false); @@ -187,12 +226,18 @@ public static async Task HttpAsync(this Uri location, HttpM /// /// Send a request as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The HTTP method. /// The Content-Type header of the HTTP request sent to the server. /// The HTTP request content sent to the server. /// The cancellation token to cancel operation. /// The task object representing the asynchronous operation. + /// + /// cannot be null -or- + /// cannot be null -or- + /// cannot be null -or- + /// cannot be null. + /// public static async Task HttpAsync(this Uri location, HttpMethod method, MediaTypeHeaderValue contentType, Stream content, CancellationToken ct = default) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpAsync(method, location, contentType, content, ct).ConfigureAwait(false); @@ -201,9 +246,12 @@ public static async Task HttpAsync(this Uri location, HttpM /// /// Send a request as an asynchronous operation. /// - /// The to request. + /// The to extend. /// The which need to be configured. /// The task object representing the asynchronous operation. + /// + /// cannot be null. + /// public static async Task HttpAsync(this Uri location, Action setup) { return await HttpManagerFactory.CreateManager(DefaultHttpClientFactory, HandlerName).HttpAsync(location, setup).ConfigureAwait(false); diff --git a/src/Cuemon.Net/Http/HttpManager.cs b/src/Cuemon.Net/Http/HttpManager.cs index bad600603..e3bb6105d 100644 --- a/src/Cuemon.Net/Http/HttpManager.cs +++ b/src/Cuemon.Net/Http/HttpManager.cs @@ -263,6 +263,7 @@ public Task HttpPatchAsync(Uri location, MediaTypeHeaderVal /// The task object representing the asynchronous operation. /// /// cannot be null -or- + /// cannot be null -or- /// cannot be null -or- /// cannot be null. /// @@ -283,6 +284,7 @@ public Task HttpAsync(HttpMethod method, Uri location, stri /// The task object representing the asynchronous operation. /// /// cannot be null -or- + /// cannot be null -or- /// cannot be null -or- /// cannot be null. /// @@ -307,10 +309,12 @@ public Task HttpAsync(HttpMethod method, Uri location, Medi /// The which need to be configured. /// The task object representing the asynchronous operation. /// + /// cannot be null -or- /// cannot be null. /// public virtual Task HttpAsync(Uri location, Action setup) { + Validator.ThrowIfNull(location, nameof(location)); Validator.ThrowIfNull(setup, nameof(setup)); var options = Patterns.Configure(setup); options.Request.RequestUri = location; From 5ee8ecd1c4639f09c5bccc16d12ab72b5f316d56 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 30 Sep 2020 23:23:55 +0200 Subject: [PATCH 238/385] Changed struct to class. --- src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt index 596021e79..7b23fff2e 100644 --- a/src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt @@ -11,7 +11,7 @@ Availability: NET Standard 2.0 - ADDED ByteArrayExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the byte[] struct: ToXmlReader - ADDED HierarchyExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the IHierarchy{T} interface: IsNodeEnumerable, GetXmlRootOrElement, OrderByXmlAttributes - ADDED StreamExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the Stream class: ToXmlReader, CopyXmlStream, TryDetectXmlEncoding -- ADDED UriExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the Uri struct: ToXmlReader +- ADDED UriExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the Uri class: ToXmlReader - ADDED XmlReaderExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the XmlReader class: ToHierarchy, MoveToFirstElement - ADDED XmlWriterExtensions class in the Cuemon.Extensions.Xml namespace that consist of extension methods for the XmlWriter class: WriteObject, WriteObject{T}, WriteStartElement, WriteEncapsulatingElementWhenNotNull{T}, WriteXmlRootElement{T}   \ No newline at end of file From ea866c3169ff71c0f0aa1afeb7d45fb915552ead Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 30 Sep 2020 23:24:43 +0200 Subject: [PATCH 239/385] Updated package description, release notes and DocFx namespace descriptions. --- .../namespaces/Cuemon.Extensions.Net.Http.md | 20 ++++++++++++++- .../Cuemon.Extensions.Net.Security.md | 20 ++++++++++++++- docfx/api/namespaces/Cuemon.Extensions.Net.md | 25 ++++++++++++++++++- docfx/toc.yml | 4 +-- .../Cuemon.Extensions.Net.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 17 +++++++++++++ 6 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 src/Cuemon.Extensions.Net/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.Net.Http.md b/docfx/api/namespaces/Cuemon.Extensions.Net.Http.md index 0efc16f9f..71f99078e 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Net.Http.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Net.Http.md @@ -2,4 +2,22 @@ uid: Cuemon.Extensions.Net.Http summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Net.Http namespace contains both types and extension methods that complements the Cuemon.Net namespace. Includes support for both traditional and factory based ways of working with HttpMangager instances while also including a simple and lightweight implementation of the IHttpClientFactory interface named SlimHttpClientFactory (that provides "managed" HttpClient instances). + +Availability: NET Standard 2.0 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Net/Http)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Net/Http)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Net/Http) + +NuGet packages 📦\ +[Cuemon.Extensions.Net (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Net)\ +[Cuemon.Extensions.Net (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Net) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|HttpMethod|⬇️|`ToHttpMethod`| +|Uri|⬇️|`HttpDeleteAsync`, `HttpGetAsync`, `HttpHeadAsync`, `HttpOptionsAsync`, `HttpPostAsync`, `HttpPutAsync`, `HttpPatchAsync`, `HttpTraceAsync`, `HttpAsync`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Net.Security.md b/docfx/api/namespaces/Cuemon.Extensions.Net.Security.md index 92597b66a..76bffe1d9 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Net.Security.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Net.Security.md @@ -2,4 +2,22 @@ uid: Cuemon.Extensions.Net.Security summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Net.Security namespace contains extension methods that provides a generic way to make a Uniform Resource Identifier signed and tampering protected. This could be used to make your own lightweight concept of a Azure shared access signatures (SAS). Originally part of Cuemon .NET Framework: https://github.com/gimlichael/CuemonNetFramework/blob/master/Cuemon.Web/Security/WebSecurityUtility.cs. Greatly simplified anno 2020. + +Availability: NET Standard 2.0 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Net/Security)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Net/Security)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Net/Security) + +NuGet packages 📦\ +[Cuemon.Extensions.Net (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Net)\ +[Cuemon.Extensions.Net (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Net) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|String|⬇️|`ToSignedUri`, `ValidateSignedUri`| +|Uri|⬇️|`ToSignedUri`, `ValidateSignedUri`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Net.md b/docfx/api/namespaces/Cuemon.Extensions.Net.md index 930022e57..7eb15d832 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Net.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Net.md @@ -2,4 +2,27 @@ uid: Cuemon.Extensions.Net summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Net namespace contains both types and extension methods that complements the Cuemon.Net namespace while being an addition to the System.Net namespace. Includes support for both traditional and factory based ways of working with HttpMangager instances while also including a simple and lightweight implementation of the IHttpClientFactory interface named SlimHttpClientFactory (that provides "managed" HttpClient instances). + +Availability: NET Standard 2.0 + +Complements: [Cuemon.Net namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Net.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Net)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Net)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Net) + +NuGet packages 📦\ +[Cuemon.Extensions.Net (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Net)\ +[Cuemon.Extensions.Net (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Net) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|byte[]|⬇️|`UrlEncode`| +|IDictionary{string, string[]}|⬇️|`ToQueryString`| +|HttpStatusCode|⬇️|`IsInformationStatusCode`, `IsSuccessStatusCode`, `IsRedirectionStatusCode`, `IsClientErrorStatusCode`, `IsServerErrorStatusCode`| +|NameValueCollection|⬇️|`ToQueryString`| +|String|⬇️|`UrlDecode`, `UrlEncode`| \ No newline at end of file diff --git a/docfx/toc.yml b/docfx/toc.yml index e80abea64..89bd30aab 100644 --- a/docfx/toc.yml +++ b/docfx/toc.yml @@ -4,12 +4,12 @@ - name: Core API href: api/dotnet topicHref: api/dotnet/index.md -- name: Extensions for Core API +- name: Extensions for Core APIs href: api/dotnet/ext topicHref: api/dotnet/ext/index.md - name: ASP.NET Core API href: api/aspnet topicHref: api/aspnet/index.md -- name: Extensions for ASP.NET Core API +- name: Extensions for ASP.NET Core APIs href: api/aspnet/ext topicHref: api/aspnet/ext/index.md \ No newline at end of file diff --git a/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj b/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj index 18d1c1f65..183916ac0 100644 --- a/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj +++ b/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Net Cuemon.Extensions.Net - The Cuemon.Extensions.Net namespace contains extension methods (query-string parsing, encoding, decoding, security and http communication) and features related to the System.Net namespace. A versatile HttpManager that transparently promotes the HttpClient is included. + The Cuemon.Extensions.Net namespace contains both types and extension methods that complements the Cuemon.Net namespace while being an addition to the System.Net namespace. Includes support for both traditional and factory based ways of working with HttpClient instances while also including a simple and lightweight implementation of the IHttpClientFactory interface named SlimHttpClientFactory. extension-methods extensions to-signed-uri validate-signed-uri http-manager-factory slim-http-client-factory i-http-client-factory diff --git a/src/Cuemon.Extensions.Net/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Net/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..a6fb93889 --- /dev/null +++ b/src/Cuemon.Extensions.Net/Properties/PackageReleaseNotes.txt @@ -0,0 +1,17 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED HttpManagerFactory class in the Cuemon.Extensions.Net.Http namespace that provides access to factory methods for creating and configuring HttpManager instances +- ADDED HttpMethodExtensions class in the Cuemon.Extensions.Net.Http namespace that consist of extension methods for the HttpMethod class: ToHttpMethod +- ADDED SlimHttpClientFactory class in the Cuemon.Extensions.Net.Http namespace that provides a simple and lightweight implementation of the IHttpClientFactory interface +- ADDED SlimHttpClientFactoryOptions class in the Cuemon.Extensions.Net.Http namespace that specifies options related to SlimHttpClientFactory +- ADDED StringExtensions class in the Cuemon.Extensions.Net.Http namespace that consist of extension methods for the Uri class: HttpDeleteAsync, HttpGetAsync, HttpHeadAsync, HttpOptionsAsync, HttpPostAsync, HttpPutAsync, HttpPatchAsync, HttpTraceAsync, HttpAsync +- ADDED SignedUriOptions class in the Cuemon.Extensions.Net.Security namespace that specifies options related to ToSignedUri extensions +- ADDED StringExtensions class in the Cuemon.Extensions.Net.Security namespace that consist of extension methods for the String class: ToSignedUri, ValidateSignedUri +- ADDED UriExtensions class in the Cuemon.Extensions.Net.Security namespace that consist of extension methods for the Uri class: ToSignedUri, ValidateSignedUri +- ADDED ByteArrayExtensions class in the Cuemon.Extensions.Net namespace that consist of extension methods for the byte[] struct: UrlEncode +- ADDED DictionaryExtensions class in the Cuemon.Extensions.Net namespace that consist of extension methods for the IDictionary{string, string[]} interface: ToQueryString +- ADDED NameValueCollectionExtensions class in the Cuemon.Extensions.Net namespace that consist of extension methods for the NameValueCollection class: ToQueryString +- ADDED StringExtensions class in the Cuemon.Extensions.Net namespace that consist of extension methods for the String class: UrlEncode, UrlDecode +  \ No newline at end of file From ab5b9e78f2b51b90dc1a4f0a31f1cce0751684d2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 30 Sep 2020 23:49:04 +0200 Subject: [PATCH 240/385] Updated package description and DocFx namespace description. --- docfx/api/namespaces/Cuemon.Extensions.IO.md | 24 ++++++++++++++++++- .../Cuemon.Extensions.IO.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 9 +++++++ 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 src/Cuemon.Extensions.IO/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.IO.md b/docfx/api/namespaces/Cuemon.Extensions.IO.md index e754b589c..54fb6fb62 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.IO.md +++ b/docfx/api/namespaces/Cuemon.Extensions.IO.md @@ -2,4 +2,26 @@ uid: Cuemon.Extensions.IO summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.IO namespace contains extension methods that complements the Cuemon.IO namespace while being an addition to the System.IO namespace. + +Availability: NET Standard 2.0, NET Standard 2.1 + +Complements: [Cuemon.IO namespace](https://docs.cuemon.net/api/dotnet/Cuemon.IO.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.IO)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.IO)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.IO) + +NuGet packages 📦\ +[Cuemon.Extensions.IO (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.IO)\ +[Cuemon.Extensions.IO (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.IO) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|byte[]|⬇️|`ToStream`, `ToStreamAsync`| +|Stream|⬇️|`Concat`, `ToCharArray`, `ToByteArray`, `ToByteArrayAsync`, `WriteAsync`, `TryDetectUnicodeEncoding`, `ToEncodedString`, `ToEncodedStringAsync`, `CompressBrotli`, `CompressBrotliAsync`, `CompressDeflate`, `CompressDeflateAsync`, `CompressGZip`, `CompressGZipAsync`, `DecompressBrotli`, `DecompressBrotliAsync`, `DecompressDeflate`, `DecompressDeflateAsync`, `DecompressGZip`, `DecompressGZipAsync`| +|String|⬇️|`ToStream`, `ToStreamAsync`, `ToTextReader`| +|TextReader|⬇️|`CopyToAsync`, `ReadAllLines`, `ReadAllLinesAsync`| \ No newline at end of file diff --git a/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj b/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj index d95280d24..60c5f1532 100644 --- a/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj +++ b/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.IO Cuemon.Extensions.IO - The Cuemon.Extensions.IO namespace contains extension methods and features related to the System.IO namespace. + The Cuemon.Extensions.IO namespace contains extension methods that complements the Cuemon.IO namespace while being an addition to the System.IO namespace. extension-methods extensions concat to-byte-array to-byte-array-async write-async to-encoded-string to-encoded-string-async compress-brotli compress-brotli-async compress-deflate compress-deflate-async compress-gzip compress-gzip-async diff --git a/src/Cuemon.Extensions.IO/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.IO/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..3332e0bb7 --- /dev/null +++ b/src/Cuemon.Extensions.IO/Properties/PackageReleaseNotes.txt @@ -0,0 +1,9 @@ +Version: 6.0.0 +Availability: NET Standard 2.0, NET Standard 2.1 +  +# New Features +- ADDED ByteArrayExtensions class in the Cuemon.Extensions.IO namespace that consist of extension methods for the byte[] struct: ToStream, ToStreamAsync +- ADDED StreamExtensions class in the Cuemon.Extensions.IO namespace that consist of extension methods for the Stream class: Concat, ToCharArray, ToByteArray, ToByteArrayAsync, WriteAsync, TryDetectUnicodeEncoding, ToEncodedString, ToEncodedStringAsync, CompressBrotli, CompressBrotliAsync, CompressDeflate, CompressDeflateAsync, CompressGZip, CompressGZipAsync, DecompressBrotli, DecompressBrotliAsync, DecompressDeflate, DecompressDeflateAsync, DecompressGZip, DecompressGZipAsync +- ADDED StringExtensions class in the Cuemon.Extensions.IO namespace that consist of extension methods for the String class: ToStream, ToStreamAsync, ToTextReader +- ADDED TextReaderExtensions class in the Cuemon.Extensions.IO namespace that consist of extension methods for the TextReader class: CopyToAsync, ReadAllLines, ReadAllLinesAsync +  \ No newline at end of file From 8aa1fd011ac54a4f36125d84330c8c1a50ca2f54 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 1 Oct 2020 23:08:54 +0200 Subject: [PATCH 241/385] Updated package description, release notes and DocFx namespace description. --- .../Cuemon.Extensions.Diagnostics.md | 22 ++++++++++++++++++- .../Cuemon.Extensions.Diagnostics.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 7 ++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 src/Cuemon.Extensions.Diagnostics/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md index ca14535db..9fa8f5757 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md @@ -2,4 +2,24 @@ uid: Cuemon.Extensions.Diagnostics summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Diagnostics namespace contains extension methods that complements the Cuemon.Diagnostics namespace while being an addition to the System.Diagnostics namespace. + +Availability: NET Standard 2.0 + +Complements: [Cuemon.Diagnostics namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Diagnostics)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Diagnostics)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Diagnostics) + +NuGet packages 📦\ +[Cuemon.Extensions.Diagnostics (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Diagnostics)\ +[Cuemon.Extensions.Diagnostics (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Diagnostics) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|ExceptionDescriptor|⬇️|`ToInsightsString`| +|FileVersionInfo|⬇️|`ToProductVersion`, `ToFileVersion`| \ No newline at end of file diff --git a/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj b/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj index e80b55256..edf663633 100644 --- a/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj +++ b/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Diagnostics Cuemon.Extensions.Diagnostics - The Cuemon.Extensions.Diagnostics namespace contains extension methods and features related to the Cuemon.Diagnostics namespace. + The Cuemon.Extensions.Diagnostics namespace contains extension methods that complements the Cuemon.Diagnostics namespace while being an addition to the System.Diagnostics namespace. extension-methods extensions to-insights-string to-product-version to-file-version diff --git a/src/Cuemon.Extensions.Diagnostics/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Diagnostics/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..76841d8ef --- /dev/null +++ b/src/Cuemon.Extensions.Diagnostics/Properties/PackageReleaseNotes.txt @@ -0,0 +1,7 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED ExceptionDescriptorExtensions class in the Cuemon.Extensions.Diagnostics namespace that consist of extension methods for the ExceptionDescriptor class: ToInsightsString +- ADDED FileVersionInfoExtensions class in the Cuemon.Extensions.Diagnostics namespace that consist of extension methods for the FileVersionInfo class: ToProductVersion, ToFileVersion +  \ No newline at end of file From dc4c6b17ad25555c88f3ca3ff89454cfa887f375 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 1 Oct 2020 23:34:06 +0200 Subject: [PATCH 242/385] Updated description, release notes and DocFx namespace description. --- .../Cuemon.Extensions.DependencyInjection.md | 21 ++++++++++++++++++- .../Cuemon.Extensions.Threading.Tasks.md | 2 ++ .../namespaces/Cuemon.Extensions.Threading.md | 2 ++ ...emon.Extensions.DependencyInjection.csproj | 6 +++--- .../Properties/PackageReleaseNotes.txt | 6 ++++++ 5 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 src/Cuemon.Extensions.DependencyInjection/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md b/docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md index e05dfd8c7..1484f2afd 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md +++ b/docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.DependencyInjection summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.DependencyInjection namespace contains extension methods that complements the Microsoft.Extensions.DependencyInjection namespace. + +Availability: NET Standard 2.0 + +Complements: [Microsoft.Extensions.DependencyInjection namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection?view=dotnet-plat-ext-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.DependencyInjection)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.DependencyInjection)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.DependencyInjection) + +NuGet packages 📦\ +[Cuemon.Extensions.DependencyInjection (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.DependencyInjection)\ +[Cuemon.Extensions.DependencyInjection (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.DependencyInjection) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IServiceCollection|⬇️|`Add`, `Add{TOptions}`, `Add{TService, TImplementation}`, `Add{TService, TImplementation, TOptions}`, `TryAdd`, `TryAdd{TOptions}`, `TryAdd{TService, TImplementation}`, `TryAdd{TService, TImplementation, TOptions}`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md b/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md index 61e38df75..ee8629624 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md @@ -6,6 +6,8 @@ The Cuemon.Extensions.Threading.Tasks namespace contains extension methods that Availability: NET Standard 2.0 +Complements: [System.Threading.Tasks namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks?view=netstandard-2.0) 🔗 + Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Threading/Tasks)\ [release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Threading/Tasks)\ diff --git a/docfx/api/namespaces/Cuemon.Extensions.Threading.md b/docfx/api/namespaces/Cuemon.Extensions.Threading.md index 23578c928..fc8c7cfad 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Threading.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Threading.md @@ -6,6 +6,8 @@ The Cuemon.Extensions.Threading namespace contains extension methods that comple Availability: NET Standard 2.0 +Complements: [System.Threading namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading?view=netstandard-2.0) 🔗 + Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Threading)\ [release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Threading)\ diff --git a/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj b/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj index 68c15e73b..4f3f822ce 100644 --- a/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj +++ b/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj @@ -8,13 +8,13 @@ Cuemon.Extensions.DependencyInjection Cuemon.Extensions.DependencyInjection - The Cuemon.Extensions.DependencyInjection namespace contains extension methods and features related to the Microsoft.Extensions.DependencyInjection namespace. + The Cuemon.Extensions.DependencyInjection namespace contains extension methods that complements the Microsoft.Extensions.DependencyInjection namespace. extension-methods extensions add tryadd - - + + diff --git a/src/Cuemon.Extensions.DependencyInjection/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.DependencyInjection/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..f13db3b13 --- /dev/null +++ b/src/Cuemon.Extensions.DependencyInjection/Properties/PackageReleaseNotes.txt @@ -0,0 +1,6 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED ServiceCollectionExtensions class in the Cuemon.Extensions.DependencyInjection namespace that consist of extension methods for the IServiceCollection interface: Add, Add{TOptions}, Add{TService, TImplementation}, Add{TService, TImplementation, TOptions}, TryAdd, TryAdd{TOptions}, TryAdd{TService, TImplementation}, TryAdd{TService, TImplementation, TOptions} +  \ No newline at end of file From 563725962d018af535f0cf3f8123fc52a4309c3b Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 1 Oct 2020 23:44:19 +0200 Subject: [PATCH 243/385] Updated description, release notes and DocFx namespace description. --- .../Cuemon.Extensions.Data.Integrity.md | 24 ++++++++++++++++++- .../api/namespaces/Cuemon.Extensions.Data.md | 22 ++++++++++++++++- .../Cuemon.Extensions.Data.Integrity.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 9 +++++++ .../Cuemon.Extensions.Data.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 7 ++++++ 6 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 src/Cuemon.Extensions.Data.Integrity/Properties/PackageReleaseNotes.txt create mode 100644 src/Cuemon.Extensions.Data/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md b/docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md index 2bcf36552..01edee9a2 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md @@ -2,4 +2,26 @@ uid: Cuemon.Extensions.Data.Integrity summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Data.Integrity namespace contains extension methods that complements the Cuemon.Data.Integrity namespace. + +Availability: NET Standard 2.0 + +Complements: [Cuemon.Data.Integrity namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Data.Integrity.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Data.Integrity)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Data.Integrity)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Data.Integrity) + +NuGet packages 📦\ +[Cuemon.Extensions.Data.Integrity (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Data.Integrity)\ +[Cuemon.Extensions.Data.Integrity (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Data.Integrity) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|Assembly|⬇️|`GetCacheValidator`| +|ChecksumBuilder|⬇️|`CombineWith{T}`| +|DateTime|⬇️|`GetCacheValidator`| +|FileInfo|⬇️|`GetCacheValidator`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Data.md b/docfx/api/namespaces/Cuemon.Extensions.Data.md index df66f2a96..c7ddc9468 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Data.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Data.md @@ -2,4 +2,24 @@ uid: Cuemon.Extensions.Data summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Data namespace contains extension methods that complements the Cuemon.Data namespace while being an addition to the System.Data namespace. + +Availability: NET Standard 2.0 + +Complements: [Cuemon.Data namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Data.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Data)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Data)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Data) + +NuGet packages 📦\ +[Cuemon.Extensions.Data (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Data)\ +[Cuemon.Extensions.Data (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Data) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IDataReader|⬇️|`ToColumns`, `ToRows`| +|QueryFormat|⬇️|`Embed`| \ No newline at end of file diff --git a/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj b/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj index 1dc479996..eb8ace305 100644 --- a/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj +++ b/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Data.Integrity Cuemon.Extensions.Data.Integrity - The Cuemon.Extensions.Data.Integrity namespace contains extension methods and features related to the Cuemon.Data.Integrity namespace. + The Cuemon.Extensions.Data.Integrity namespace contains extension methods that complements the Cuemon.Data.Integrity namespace. extension-methods extensions get-cache-validator combine-with diff --git a/src/Cuemon.Extensions.Data.Integrity/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Data.Integrity/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..f1edc1978 --- /dev/null +++ b/src/Cuemon.Extensions.Data.Integrity/Properties/PackageReleaseNotes.txt @@ -0,0 +1,9 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED AssemblyExtensions class in the Cuemon.Extensions.Data.Integrity namespace that consist of extension methods for the Assembly class: GetCacheValidator +- ADDED ChecksumBuilderExtensions class in the Cuemon.Extensions.Data.Integrity namespace that consist of extension methods for the ChecksumBuilder class: CombineWith{T} +- ADDED DateTimeExtensions class in the Cuemon.Extensions.Data.Integrity namespace that consist of extension methods for the DateTime struct: GetCacheValidator +- ADDED FileInfoExtensions class in the Cuemon.Extensions.Data.Integrity namespace that consist of extension methods for the FileInfo class: GetCacheValidator +  \ No newline at end of file diff --git a/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj b/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj index 4cf38741f..12ab67377 100644 --- a/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj +++ b/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj @@ -9,7 +9,7 @@ Cuemon Cuemon.Extensions.Data Cuemon.Extensions.Data - The Cuemon.Extensions.Data namespace contains extension methods and features related to the Cuemon.Data namespace. + The Cuemon.Extensions.Data namespace contains extension methods that complements the Cuemon.Data namespace while being an addition to the System.Data namespace. extension-methods extensions to-rows to-columns embed diff --git a/src/Cuemon.Extensions.Data/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Data/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..f95e18ddf --- /dev/null +++ b/src/Cuemon.Extensions.Data/Properties/PackageReleaseNotes.txt @@ -0,0 +1,7 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED DataTransferExtensions class in the Cuemon.Extensions.Data namespace that consist of extension methods for the IDataReader interface: ToColumns, ToRows +- ADDED QueryFormatExtensions class in the Cuemon.Extensions.Data namespace that consist of extension methods for the QueryFormat enum: Embed +  \ No newline at end of file From bee98b3d688b9d4f7c8a5db97acebee31e4658ed Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 01:59:21 +0200 Subject: [PATCH 244/385] Changed Throw to ThrowIf and adjusted naming of method extensions. --- src/Cuemon.Core/Validator.cs | 4 +- .../GlobalSuppressions.cs | 6 +-- .../ValidatorExtensions.cs | 52 +------------------ .../GlobalSuppressions.cs | 2 +- .../JData.cs | 2 +- .../ValidatorExtensions.cs | 2 +- .../ValidatorExtensionsTest.cs | 22 +------- 7 files changed, 11 insertions(+), 79 deletions(-) diff --git a/src/Cuemon.Core/Validator.cs b/src/Cuemon.Core/Validator.cs index 391f8b92e..3e879d66a 100644 --- a/src/Cuemon.Core/Validator.cs +++ b/src/Cuemon.Core/Validator.cs @@ -15,10 +15,10 @@ public sealed class Validator private static readonly Validator ExtendedValidator = new Validator(); /// - /// Gets the singleton instance of the Validator functionality allowing for extensions methods like: Validator.Throw.IfNotValidJsonDocument(). + /// Gets the singleton instance of the Validator functionality allowing for extensions methods like: Validator.ThrowIf.InvalidJsonDocument(). /// /// The singleton instance of the Validator functionality. - public static Validator Throw { get; } = ExtendedValidator; + public static Validator ThrowIf { get; } = ExtendedValidator; /// /// Validates and throws an (or a derived counterpart) from the specified delegate . diff --git a/src/Cuemon.Extensions.Core/GlobalSuppressions.cs b/src/Cuemon.Extensions.Core/GlobalSuppressions.cs index d4253e350..60bba6569 100644 --- a/src/Cuemon.Extensions.Core/GlobalSuppressions.cs +++ b/src/Cuemon.Extensions.Core/GlobalSuppressions.cs @@ -4,7 +4,5 @@ // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Extensions.ValidatorExtensions.IfHasDifference(Cuemon.Validator,System.String,System.String,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Extensions.ValidatorExtensions.IfHasDistinctDifference(Cuemon.Validator,System.String,System.String,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Extensions.ValidatorExtensions.IfHasNotDifference(Cuemon.Validator,System.String,System.String,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Extensions.ValidatorExtensions.IfHasNotDistinctDifference(Cuemon.Validator,System.String,System.String,System.String,System.String)")] \ No newline at end of file +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Extensions.ValidatorExtensions.HasDifference(Cuemon.Validator,System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Extensions.ValidatorExtensions.NoDifference(Cuemon.Validator,System.String,System.String,System.String,System.String)")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Extensions.ValidatorExtensions.IfHasNotDistinctDifference(Cuemon.Validator,System.String,System.String,System.String,System.String)")] \ No newline at end of file diff --git a/src/Cuemon.Extensions.Core/ValidatorExtensions.cs b/src/Cuemon.Extensions.Core/ValidatorExtensions.cs index e3d29cc6e..e72933724 100644 --- a/src/Cuemon.Extensions.Core/ValidatorExtensions.cs +++ b/src/Cuemon.Extensions.Core/ValidatorExtensions.cs @@ -18,7 +18,7 @@ public static class ValidatorExtensions /// /// There is a difference between and . /// - public static void IfHasDifference(this Validator validator, string first, string second, string paramName, string message = null) + public static void HasDifference(this Validator validator, string first, string second, string paramName, string message = null) { if (message == null) { message = FormattableString.Invariant($"Specified arguments has a difference between {nameof(second)} and {nameof(first)}."); } try @@ -31,30 +31,6 @@ public static void IfHasDifference(this Validator validator, string first, strin } } - /// - /// Validates and throws an if there is a distinct difference between and . - /// - /// The to extend. - /// The value that specifies valid characters. - /// The value to distinctively compare with . - /// The name of the parameter that caused the exception. - /// A message that describes the error. - /// - /// There is a distinct difference between and . - /// - public static void IfHasDistinctDifference(this Validator validator, string first, string second, string paramName, string message = null) - { - if (message == null) { message = FormattableString.Invariant($"Specified arguments has a distinct difference between {nameof(second)} and {nameof(first)}."); } - try - { - validator.ThrowWhenCondition(c => c.IsTrue((out string invalidCharacters) => Condition.Query.HasDistinctDifference(first, second, out invalidCharacters)).Create(invalidCharacters => new ArgumentOutOfRangeException(paramName, invalidCharacters, message)).TryThrow()); - } - catch (ArgumentOutOfRangeException ex) - { - throw ex; - } - } - /// /// Validates and throws an if there is no difference between and . /// @@ -66,7 +42,7 @@ public static void IfHasDistinctDifference(this Validator validator, string firs /// /// There is no difference between and . /// - public static void IfHasNotDifference(this Validator validator, string first, string second, string paramName, string message = null) + public static void NoDifference(this Validator validator, string first, string second, string paramName, string message = null) { if (message == null) { message = FormattableString.Invariant($"Specified arguments does not have a difference between {nameof(second)} and {nameof(first)}."); } try @@ -78,29 +54,5 @@ public static void IfHasNotDifference(this Validator validator, string first, st throw ex; } } - - /// - /// Validates and throws an if there is not a distinct difference between and . - /// - /// The to extend. - /// The value that specifies valid characters. - /// The value to distinctively compare with . - /// The name of the parameter that caused the exception. - /// A message that describes the error. - /// - /// There is not a distinct difference between and . - /// - public static void IfHasNotDistinctDifference(this Validator validator, string first, string second, string paramName, string message = null) - { - if (message == null) { message = FormattableString.Invariant($"Specified arguments does not have a distinct difference between {nameof(second)} and {nameof(first)}."); } - try - { - validator.ThrowWhenCondition(c => c.IsFalse(() => Condition.Query.HasDistinctDifference(first, second, out _)).Create(() => new ArgumentOutOfRangeException(paramName, message)).TryThrow()); - } - catch (ArgumentOutOfRangeException ex) - { - throw ex; - } - } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs b/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs index 695b4cbba..ffbb6f1ba 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/GlobalSuppressions.cs @@ -5,6 +5,6 @@ using System.Diagnostics.CodeAnalysis; -[assembly: SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.ValidatorExtensions.IfNotValidJsonDocument(Cuemon.Validator,Newtonsoft.Json.JsonReader@,System.String,System.String)")] +[assembly: SuppressMessage("Major Code Smell", "S3445:Exceptions should not be explicitly rethrown", Justification = "This is by design; we only want the stacktrace from within the validator method.", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.ValidatorExtensions.InvalidJsonDocument(Cuemon.Validator,Newtonsoft.Json.JsonReader@,System.String,System.String)")] [assembly: SuppressMessage("Major Code Smell", "S907:\"goto\" statement should not be used", Justification = "Legacy code ;-)", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.JsonReaderExtensions.ToHierarchy(Newtonsoft.Json.JsonReader)~Cuemon.IHierarchy{Cuemon.DataPair}")] [assembly: SuppressMessage("Critical Code Smell", "S3776:Cognitive Complexity of methods should not be too high", Justification = "Legacy code ;-)", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.JsonReaderExtensions.ToHierarchy(Newtonsoft.Json.JsonReader)~Cuemon.IHierarchy{Cuemon.DataPair}")] diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/JData.cs b/src/Cuemon.Extensions.Newtonsoft.Json/JData.cs index 2329036fe..6d3d9ed9e 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/JData.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/JData.cs @@ -57,7 +57,7 @@ public static IEnumerable ReadAll(string json) public static IEnumerable ReadAll(JsonReader reader) { Validator.ThrowIfNull(reader, nameof(reader)); - Validator.Throw.IfNotValidJsonDocument(ref reader, nameof(reader)); + Validator.ThrowIf.InvalidJsonDocument(ref reader, nameof(reader)); return new JData(reader).Result.Value; } diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/ValidatorExtensions.cs b/src/Cuemon.Extensions.Newtonsoft.Json/ValidatorExtensions.cs index d79ad4d48..905fb57d5 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/ValidatorExtensions.cs +++ b/src/Cuemon.Extensions.Newtonsoft.Json/ValidatorExtensions.cs @@ -19,7 +19,7 @@ public static class ValidatorExtensions /// /// must be a JSON representation that complies with RFC 8259. /// - public static void IfNotValidJsonDocument(this Validator validator, ref JsonReader value, string paramName, string message = "Value must be a JSON representation that complies with RFC 8259.") + public static void InvalidJsonDocument(this Validator validator, ref JsonReader value, string paramName, string message = "Value must be a JSON representation that complies with RFC 8259.") { if (value == null) { return; } var reader = value; diff --git a/test/Cuemon.Extensions.Core.Tests/ValidatorExtensionsTest.cs b/test/Cuemon.Extensions.Core.Tests/ValidatorExtensionsTest.cs index 4fb3c35dc..397fdc04c 100644 --- a/test/Cuemon.Extensions.Core.Tests/ValidatorExtensionsTest.cs +++ b/test/Cuemon.Extensions.Core.Tests/ValidatorExtensionsTest.cs @@ -12,30 +12,12 @@ public ValidatorExtensionsTest(ITestOutputHelper output) : base(output) } - [Fact] - public void IfHasDistinctDifference_ShouldThrowArgumentOutOfRangeException() - { - Assert.Throws(() => - { - Validator.Throw.IfHasDistinctDifference("aaabbbccc", "dddeeefff", "paramName"); - }); - } - - [Fact] - public void IfHasNotDistinctDifference_ShouldThrowArgumentOutOfRangeException() - { - Assert.Throws(() => - { - Validator.Throw.IfHasNotDistinctDifference("aaabbbccc", "cccbbbbaaaa", "paramName"); - }); - } - [Fact] public void IfHasDifference_ShouldThrowArgumentOutOfRangeException() { Assert.Throws(() => { - Validator.Throw.IfHasDifference("aaabbbccc", "dddeeefff", "paramName"); + Validator.ThrowIf.HasDifference("aaabbbccc", "dddeeefff", "paramName"); }); } @@ -44,7 +26,7 @@ public void IfHasNotDifference_ShouldThrowArgumentOutOfRangeException() { Assert.Throws(() => { - Validator.Throw.IfHasNotDifference("aaabbbccc", "cccbbbbaaaa", "paramName"); + Validator.ThrowIf.NoDifference("aaabbbccc", "cccbbbbaaaa", "paramName"); }); } } From f24a8480fed200e2a1d203b812e8c9e10ee3db84 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 02:00:16 +0200 Subject: [PATCH 245/385] Added unit tests to core extensions. --- .../Cuemon.Core.Tests.csproj | 4 - .../Cuemon.Extensions.Core.Tests.csproj | 1 + .../ObjectExtensionsTest.cs | 94 +++++++++ .../StringExtensionsTest.cs | 199 ++++++++++++++++++ .../TimeSpanExtensionsTest.cs | 54 +++++ .../TypeExtensionsTest.cs | 188 +++++++++++++++++ 6 files changed, 536 insertions(+), 4 deletions(-) create mode 100644 test/Cuemon.Extensions.Core.Tests/ObjectExtensionsTest.cs create mode 100644 test/Cuemon.Extensions.Core.Tests/StringExtensionsTest.cs create mode 100644 test/Cuemon.Extensions.Core.Tests/TimeSpanExtensionsTest.cs create mode 100644 test/Cuemon.Extensions.Core.Tests/TypeExtensionsTest.cs diff --git a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj index cda6aee4c..957fa6fb4 100644 --- a/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj +++ b/test/Cuemon.Core.Tests/Cuemon.Core.Tests.csproj @@ -21,8 +21,4 @@ - - - - \ No newline at end of file diff --git a/test/Cuemon.Extensions.Core.Tests/Cuemon.Extensions.Core.Tests.csproj b/test/Cuemon.Extensions.Core.Tests/Cuemon.Extensions.Core.Tests.csproj index 2e950d328..32bfd9949 100644 --- a/test/Cuemon.Extensions.Core.Tests/Cuemon.Extensions.Core.Tests.csproj +++ b/test/Cuemon.Extensions.Core.Tests/Cuemon.Extensions.Core.Tests.csproj @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/test/Cuemon.Extensions.Core.Tests/ObjectExtensionsTest.cs b/test/Cuemon.Extensions.Core.Tests/ObjectExtensionsTest.cs new file mode 100644 index 000000000..dee949a99 --- /dev/null +++ b/test/Cuemon.Extensions.Core.Tests/ObjectExtensionsTest.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +using System.Linq; +using Cuemon.Collections.Generic; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Extensions +{ + public class ObjectExtensionsTest : Test + { + public ObjectExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void UseWrapper_ShouldWrapTheAnswerToEverything() + { + var si = 42.UseWrapper(info => info.Add("funFact", "THE ANSWER TO LIFE, THE UNIVERSE AND EVERYTHING")); + + Assert.Equal(42, si.Instance); + Assert.Equal(typeof(int), si.InstanceType); + Assert.Equal(typeof(long), si.InstanceAs().GetType()); + Assert.Equal(typeof(byte), si.InstanceAs().GetType()); + Assert.Equal("THE ANSWER TO LIFE, THE UNIVERSE AND EVERYTHING", si.Data.Single(pair => pair.Key == "funFact").Value); + } + + [Fact] + public void As_ConvertAnythingOrDefault() + { + object answer = 42; + Assert.Equal("42", 42.As()); + Assert.Equal(42, 42.As()); + Assert.Equal(42, 42.As()); + Assert.Equal(42, 42.As()); + Assert.Equal(42, 42.As()); + Assert.Equal((ulong)42, 42.As()); + Assert.Equal((uint)42, 42.As()); + Assert.Equal(42, 42.As()); + Assert.Equal(42, 42.As()); + Assert.Equal(TimeSpan.FromTicks(42), 42.As(TimeSpan.FromTicks(42))); + } + + [Fact] + public void GetHashCode32_ShouldGenerateASuitable32bitHashCode() + { + var v = Arguments.ToEnumerableOf(42, Guid.Empty.ToString("N"), DateTime.MaxValue, TimeSpan.TicksPerDay, true, decimal.MaxValue, double.Epsilon, float.Epsilon); + Assert.Equal(1246074942, v.GetHashCode32()); + } + + [Fact] + public void GetHashCode64_ShouldGenerateASuitable32bitHashCode() + { + var v = Arguments.ToEnumerableOf(42, Guid.Empty.ToString("N"), DateTime.MaxValue, TimeSpan.TicksPerDay, true, decimal.MaxValue, double.Epsilon, float.Epsilon); + Assert.Equal(6647510224603551806, v.GetHashCode64()); + } + + [Fact] + public void ToDelimitedString_ShouldGenerateDelimitedString() + { + var s = Generate.RangeOf(10, i => i); + var ds = s.ToDelimitedString(); + + Assert.Equal("0,1,2,3,4,5,6,7,8,9", ds); + } + + [Fact] + public void Adjust_ShouldChangeTheStateOfExistingInstance() + { + var dt1 = DateTime.MinValue; + var dt2 = dt1.Adjust(i => DateTime.MaxValue); + + Assert.Equal(dt1, DateTime.MinValue); + Assert.Equal(dt2, DateTime.MaxValue); + } + + [Fact] + public void IsNullable_ShouldBeFalse() + { + var dt1 = DateTime.MinValue; + + Assert.False(dt1.IsNullable()); + } + + [Fact] + public void IsNullable_ShouldBeTrue() + { + DateTime? dt1 = null; + + Assert.True(dt1.IsNullable()); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Core.Tests/StringExtensionsTest.cs b/test/Cuemon.Extensions.Core.Tests/StringExtensionsTest.cs new file mode 100644 index 000000000..b2b591c11 --- /dev/null +++ b/test/Cuemon.Extensions.Core.Tests/StringExtensionsTest.cs @@ -0,0 +1,199 @@ +using System; +using System.Globalization; +using System.Linq; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Extensions +{ + public class StringExtensionsTest : Test + { + public StringExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Difference_ShouldGetDifference() + { + var s1 = Alphanumeric.UppercaseLetters; + var s2 = Alphanumeric.Letters; + var s3 = s1.Difference(s2); + + Assert.Equal(Alphanumeric.LowercaseLetters, s3); + } + + [Fact] + public void ToCharArray_ShouldConvertStringToCharArray() + { + var s1 = Alphanumeric.LettersAndNumbers; + var c1 = s1.ToCharArray(); + + Assert.Equal(s1, string.Concat(c1)); + } + + [Fact] + public void ToByteArray_ShouldConvertStringToByteArray() + { + var s1 = Alphanumeric.LettersAndNumbers; + var b1 = s1.ToByteArray(); + + Assert.Equal(s1, Convertible.ToString(b1)); + } + + [Fact] + public void FromUrlEncodedBase64String_ShouldConvertUrlEncodedBase64StringToByteArray() + { + var s1 = "VGhpcyBpcyBhIHRlc3Qgd2l0aCBzcGVjaWFsIGNoYXJhY3RlcnMgISIjwqQlJi8oKUAhPQ"; + var b1 = s1.FromUrlEncodedBase64(); + var s2 = Convertible.ToString(b1); + + Assert.Equal("This is a test with special characters !\"#¤%&/()@!=", s2); + } + + [Fact] + public void FromBinaryDigits_ShouldConvertBinaryDigitsStringToByteArray() + { + var s1 = "01010100011010000110100101110011001000000110100101110011001000000110000100100000011101000110010101110011011101000010000001110111011010010111010001101000001000000111001101110000011001010110001101101001011000010110110000100000011000110110100001100001011100100110000101100011011101000110010101110010011100110010000000100001001000100010001111000010101001000010010100100110001011110010100000101001010000000010000100111101"; + var b1 = s1.FromBinaryDigits(); + var s2 = Convertible.ToString(b1); + + Assert.Equal("This is a test with special characters !\"#¤%&/()@!=", s2); + } + + [Fact] + public void FromBase64_ShouldConvertBase64StringToByteArray() + { + var s1 = "VGhpcyBpcyBhIHRlc3Qgd2l0aCBzcGVjaWFsIGNoYXJhY3RlcnMgISIjwqQlJi8oKUAhPQ=="; + var b1 = s1.FromBase64(); + var s2 = Convertible.ToString(b1); + + Assert.Equal("This is a test with special characters !\"#¤%&/()@!=", s2); + } + + [Fact] + public void ToCasing_ShouldConvertToDifferentCasingMethods() + { + var s1 = "Cuemon for .net"; + var s2 = s1.ToCasing(); + var s3 = s1.ToCasing(CasingMethod.LowerCase); + var s4 = s1.ToCasing(CasingMethod.TitleCase); + var s5 = s1.ToCasing(CasingMethod.UpperCase); + + Assert.Equal(s1, s2); + Assert.Equal(s1.ToLowerInvariant(), s3); + Assert.Equal(CultureInfo.InvariantCulture.TextInfo.ToTitleCase(s1), s4); + Assert.Equal(s1.ToUpperInvariant(), s5); + } + + [Fact] + public void ToUri_ShouldConvertToUri() + { + var s1 = "https://www.cuemon.net/"; + var u1 = s1.ToUri(); + + Assert.Equal(s1, u1.OriginalString); + } + + [Fact] + public void IsNullOrEmpty_ShouldGiveFalseOnAtLeastOneNullOrEmpty() + { + var l1 = Generate.RangeOf(5, i => + { + if (i < 4) { return $"{i}"; } + return null; + }); + + var l2 = Generate.RangeOf(5, i => + { + if (i < 4) { return $"{i}"; } + return ""; + }); + + var l3 = Generate.RangeOf(5, i => $"{i}"); + + Assert.True(l1.IsNullOrEmpty()); + Assert.True(l2.IsNullOrEmpty()); + Assert.False(l3.IsNullOrEmpty()); + } + + [Fact] + public void Count_ShouldCountSpecifiedChar() + { + Assert.Equal(10, string.Concat(Alphanumeric.LettersAndNumbers, new string('a', 9)).Count('a')); + Assert.Equal(10, string.Concat(Alphanumeric.LettersAndNumbers, new string('Z', 9)).Count('Z')); + } + + [Fact] + public void JsEscape_ShouldDoJavascriptEscape() + { + Assert.Equal("Need%20complemental%20framework%20for%20.NET%3F%20Use%20Cuemon%20for%20.NET%21", "Need complemental framework for .NET? Use Cuemon for .NET!".JsEscape()); + } + + [Fact] + public void JsUnescape_ShouldDoJavascriptUnescape() + { + Assert.Equal("Need complemental framework for .NET? Use Cuemon for .NET!", "Need%20complemental%20framework%20for%20.NET%3F%20Use%20Cuemon%20for%20.NET%21".JsUnescape()); + } + + private static readonly string SentenceWithCuemonInIt = "Cuemon is following most of the Framework Design Guidelines."; + private static readonly string SentenceWithForInIt = "Let's live for freedom!"; + private static readonly string SentenceWithDotNetInIt = "Microsoft .NET - one platform to rule them all."; + + [Fact] + public void ContainsAny() + { + Assert.True("Cuemon for .NET".ContainsAny(SentenceWithCuemonInIt.Split(' '))); + Assert.True("Cuemon for .NET".ContainsAny(SentenceWithForInIt.Split(' '))); + Assert.True("Cuemon for .NET".ContainsAny(SentenceWithDotNetInIt.Split(' '))); + Assert.False("Cuemon for .NET".ContainsAny("some", "random", "words")); + } + + [Fact] + public void ContainsAll() + { + Assert.True("Cuemon for .NET".ContainsAll("Cuemon", "for", ".NET")); + Assert.False("Cuemon for .NET".ContainsAll("Cuemon", "for", "all")); + } + + [Fact] + public void EqualsAny() + { + Assert.True("Cuemon for .NET".EqualsAny("some sentence", "Cuemon for .NET")); + Assert.False("Cuemon for .NET".EqualsAny(SentenceWithCuemonInIt.Split(' '))); + Assert.False("Cuemon for .NET".EqualsAny(SentenceWithForInIt.Split(' '))); + Assert.False("Cuemon for .NET".EqualsAny(SentenceWithDotNetInIt.Split(' '))); + Assert.False("Cuemon for .NET".EqualsAny("some", "random", "words")); + } + + [Fact] + public void StartsWith() + { + Assert.True("Cuemon for .NET".StartsWith("some sentence", "Cuemon for .NET")); + Assert.True("Cuemon for .NET".StartsWith(SentenceWithCuemonInIt.Split(' '))); + Assert.False("Cuemon for .NET".StartsWith(SentenceWithForInIt.Split(' '))); + Assert.False("Cuemon for .NET".StartsWith(SentenceWithDotNetInIt.Split(' '))); + Assert.False("Cuemon for .NET".StartsWith("some", "random", "words")); + } + + [Fact] + public void ToGuid_ShouldConvertStringToGuid() + { + var g1 = Guid.NewGuid(); + var s1 = g1.ToString("N"); + var g2 = s1.ToGuid(o => o.Formats = GuidFormats.N); + + Assert.Equal(g1, g2); + } + + [Fact] + public void SplitDelimited_ShouldRevertDelimitedStringToSequence() + { + var i = Generate.RangeOf(10, i1 => i1.ToString()); + var s = "0,1,2,3,4,5,6,7,8,9".SplitDelimited().ToList(); + + Assert.Equal(10, s.Count); + Assert.True(s.SequenceEqual(i)); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Core.Tests/TimeSpanExtensionsTest.cs b/test/Cuemon.Extensions.Core.Tests/TimeSpanExtensionsTest.cs new file mode 100644 index 000000000..20cfaf701 --- /dev/null +++ b/test/Cuemon.Extensions.Core.Tests/TimeSpanExtensionsTest.cs @@ -0,0 +1,54 @@ +using System; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Extensions +{ + public class TimeSpanExtensionsTest : Test + { + public TimeSpanExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void GetTotalNanoseconds_ShouldConvertTicksToNanoseconds() + { + var ts = TimeSpan.FromHours(1); + Assert.Equal(3.6E+12, ts.GetTotalNanoseconds()); + } + + [Fact] + public void GetTotalMicroseconds_ShouldConvertTicksToMicroseconds() + { + var ts = TimeSpan.FromHours(1); + Assert.Equal(3.6E+9, ts.GetTotalMicroseconds()); + } + + [Fact] + public void Floor_ShouldRoundDownToNearestUnit() + { + var ts1 = TimeSpan.FromMinutes(280); + var ts2 = TimeSpan.FromMinutes(45); + + Assert.Equal(ts1.Floor(1, TimeUnit.Hours), ts1.Floor(TimeSpan.FromHours(1))); + Assert.Equal(TimeSpan.FromHours(4), ts1.Floor(1, TimeUnit.Hours)); + + Assert.Equal(ts2.Floor(1, TimeUnit.Hours), ts2.Floor(TimeSpan.FromHours(1))); + Assert.Equal(TimeSpan.FromHours(0), ts2.Floor(1, TimeUnit.Hours)); + } + + [Fact] + public void Ceiling_ShouldRoundUpToNearestUnit() + { + var ts1 = TimeSpan.FromMinutes(280); + var ts2 = TimeSpan.FromMinutes(45); + + Assert.Equal(ts1.Ceiling(1, TimeUnit.Hours), ts1.Ceiling(TimeSpan.FromHours(1))); + Assert.Equal(TimeSpan.FromHours(5), ts1.Ceiling(1, TimeUnit.Hours)); + + Assert.Equal(ts2.Ceiling(1, TimeUnit.Hours), ts2.Ceiling(TimeSpan.FromHours(1))); + Assert.Equal(TimeSpan.FromHours(1), ts2.Ceiling(1, TimeUnit.Hours)); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Core.Tests/TypeExtensionsTest.cs b/test/Cuemon.Extensions.Core.Tests/TypeExtensionsTest.cs new file mode 100644 index 000000000..3f1f6b682 --- /dev/null +++ b/test/Cuemon.Extensions.Core.Tests/TypeExtensionsTest.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Reflection.Metadata; +using System.Text; +using System.Xml.Serialization; +using Cuemon.Extensions.Xunit; +using Cuemon.Xml.Serialization; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Extensions +{ + public class TypeExtensionsTest : Test + { + public TypeExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void ToFriendlyName_ShouldConvertTypeToHumanFriendlyRepresentation() + { + var type = typeof(IList); + var typeFriendlyString = type.ToFriendlyName(); + var typeFriendlyFqString = type.ToFriendlyName(o => o.FullName = true); + + Assert.Equal("IList", typeFriendlyString); + Assert.Equal("System.Collections.Generic.IList", typeFriendlyFqString); + } + + [Fact] + public void ToTypeCode_ShouldConvertTypeToCorrectTypeCode() + { + Type o = null; + Assert.Equal(TypeCode.Boolean, typeof(bool).ToTypeCode()); + Assert.Equal(TypeCode.Byte, typeof(byte).ToTypeCode()); + Assert.Equal(TypeCode.Char, typeof(char).ToTypeCode()); + Assert.Equal(TypeCode.DBNull, typeof(DBNull).ToTypeCode()); + Assert.Equal(TypeCode.DateTime, typeof(DateTime).ToTypeCode()); + Assert.Equal(TypeCode.Decimal, typeof(decimal).ToTypeCode()); + Assert.Equal(TypeCode.Double, typeof(double).ToTypeCode()); + Assert.Equal(TypeCode.Empty, o.ToTypeCode()); + Assert.Equal(TypeCode.Int16, typeof(short).ToTypeCode()); + Assert.Equal(TypeCode.Int32, typeof(int).ToTypeCode()); + Assert.Equal(TypeCode.Int64, typeof(long).ToTypeCode()); + Assert.Equal(TypeCode.Object, typeof(object).ToTypeCode()); + Assert.Equal(TypeCode.SByte, typeof(sbyte).ToTypeCode()); + Assert.Equal(TypeCode.Single, typeof(float).ToTypeCode()); + Assert.Equal(TypeCode.String, typeof(string).ToTypeCode()); + Assert.Equal(TypeCode.UInt16, typeof(ushort).ToTypeCode()); + Assert.Equal(TypeCode.UInt32, typeof(uint).ToTypeCode()); + Assert.Equal(TypeCode.UInt64, typeof(ulong).ToTypeCode()); + } + + [Fact] + public void HasEqualityComparerImplementation() + { + var truetype = typeof(StringComparer); + var falseType = typeof(ArgumentException); + Assert.True(truetype.HasEqualityComparerImplementation()); + Assert.False(falseType.HasEqualityComparerImplementation()); + } + + [Fact] + public void HasComparableImplementation() + { + var truetype = typeof(string); + var falseType = typeof(ArgumentException); + Assert.True(truetype.HasComparableImplementation()); + Assert.False(falseType.HasComparableImplementation()); + } + + [Fact] + public void HasComparerImplementation() + { + var truetype = typeof(HandleComparer); + var falseType = typeof(ArgumentException); + Assert.True(truetype.HasComparerImplementation()); + Assert.False(falseType.HasComparerImplementation()); + } + + [Fact] + public void HasEnumerableImplementation() + { + var truetype = typeof(ConcurrentBag<>); + var falseType = typeof(ArgumentException); + Assert.True(truetype.HasEnumerableImplementation()); + Assert.False(falseType.HasEnumerableImplementation()); + } + + [Fact] + public void HasDictionaryImplementation() + { + var truetype = typeof(ConcurrentDictionary<,>); + var falseType = typeof(List<>); + Assert.True(truetype.HasDictionaryImplementation()); + Assert.False(falseType.HasDictionaryImplementation()); + } + + [Fact] + public void HasKeyValuePairImplementation() + { + var truetype = typeof(KeyValuePair<,>); + var falseType = typeof(List<>); + Assert.True(truetype.HasKeyValuePairImplementation()); + Assert.False(falseType.HasKeyValuePairImplementation()); + } + + [Fact] + public void IsNullable() + { + var truetype = typeof(int?); + var falseType = typeof(int); + Assert.True(truetype.IsNullable()); + Assert.False(falseType.IsNullable()); + } + + [Fact] + public void HasAnonymousCharacteristics() + { + var truetype = new Func(s => s).Target.GetType(); + var falseType = typeof(int); + Assert.True(truetype.HasAnonymousCharacteristics()); + Assert.False(falseType.HasAnonymousCharacteristics()); + } + + [Fact] + public void IsComplex() + { + var truetype = typeof(Stream); + var falseType = typeof(int); + Assert.True(truetype.IsComplex()); + Assert.False(falseType.IsComplex()); + } + + [Fact] + public void IsSimple() + { + var truetype = typeof(int); + var falseType = typeof(Stream); + Assert.True(truetype.IsSimple()); + Assert.False(falseType.IsSimple()); + } + + [Fact] + public void GetDefaultValue_ShouldBeDefaultValueFromValueTypesAndReferenceTypesWithDefaultConstructor() + { + Assert.Equal(0, typeof(int).GetDefaultValue()); + Assert.Equal(decimal.Zero, typeof(decimal).GetDefaultValue()); + Assert.Equal(Guid.Empty, typeof(Guid).GetDefaultValue()); + Assert.Equal(DateTime.MinValue, typeof(DateTime).GetDefaultValue()); + Assert.Equal(TimeSpan.Zero, typeof(TimeSpan).GetDefaultValue()); + Assert.Null(typeof(int?).GetDefaultValue()); + Assert.Null(typeof(bool?).GetDefaultValue()); + } + + [Fact] + public void HasTypes_ShouldBeTrueForThoseImplementingTheTypeAndFalseForRest() + { + Assert.True(typeof(FileStream).HasTypes(typeof(Stream))); + Assert.True(typeof(MemoryStream).HasTypes(typeof(MarshalByRefObject))); + Assert.False(typeof(StringBuilder).HasTypes(typeof(string))); + Assert.False(typeof(UTF32Encoding).HasTypes(typeof(string))); + } + + [Fact] + public void HasInterfaces_ShouldBeTrueForThoseImplementingTheInterfaceAndFalseForRest() + { + Assert.True(typeof(FileStream).HasInterfaces(typeof(IDisposable))); + Assert.True(typeof(List<>).HasInterfaces(typeof(IEnumerable<>))); + Assert.False(typeof(StringBuilder).HasInterfaces(typeof(IDisposable))); + Assert.False(typeof(UTF32Encoding).HasInterfaces(typeof(IEnumerable<>))); + } + + [Fact] + public void HasAttributes_ShouldBeTrueForThoseMembersImplementingTheAttributesAndFalseForRest() + { + Assert.True(typeof(DisplayAttribute).HasAttributes(typeof(AttributeUsageAttribute))); + Assert.True(typeof(XmlWrapper).HasAttributes(typeof(XmlIgnoreAttribute))); + Assert.True(typeof(StringBuilder).HasAttributes(typeof(SerializableAttribute))); + Assert.True(typeof(UTF32Encoding).HasAttributes(typeof(CLSCompliantAttribute))); + Assert.False(typeof(int).HasAttributes(typeof(AttributeUsageAttribute))); + Assert.False(typeof(string).HasAttributes(typeof(AttributeUsageAttribute))); + } + } +} \ No newline at end of file From 8380f9b840010f130d18a6eade0c4c27b9cca40f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 02:00:36 +0200 Subject: [PATCH 246/385] Removed redundant method. --- .../ConditionExtensions.cs | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/Cuemon.Extensions.Core/ConditionExtensions.cs b/src/Cuemon.Extensions.Core/ConditionExtensions.cs index bde1b8ae3..039b91def 100644 --- a/src/Cuemon.Extensions.Core/ConditionExtensions.cs +++ b/src/Cuemon.Extensions.Core/ConditionExtensions.cs @@ -10,33 +10,17 @@ public static class ConditionExtensions /// /// Determines whether there is a set difference between and . /// - /// The to extend. + /// The to extend. /// The value where characters that are not also in will be returned. /// The value to compare with . /// The set difference between and or if no difference. /// /// true if there is a set difference between and ; otherwise false. /// - public static bool HasDifference(this Condition condition, string first, string second, out string difference) + public static bool HasDifference(this Condition _, string first, string second, out string difference) { difference = first.Difference(second); return difference.Any(); } - - /// - /// Determines whether there is a distinct set difference between and . - /// - /// The to extend. - /// The value where distinct characters that are not also in will be returned. - /// The value to distinctively compare with . - /// The distinct set difference between and or if no difference. - /// - /// true if there is a distinct set difference between and ; otherwise false. - /// - public static bool HasDistinctDifference(this Condition condition, string first, string second, out string difference) - { - difference = first.DistinctDifference(second); - return difference.Any(); - } } } \ No newline at end of file From 79b2963a0e08f48e4bbcaa1f0c8d646e406cb74f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 02:00:54 +0200 Subject: [PATCH 247/385] Removed constraints. --- .../ObjectExtensions.cs | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/Cuemon.Extensions.Core/ObjectExtensions.cs b/src/Cuemon.Extensions.Core/ObjectExtensions.cs index d4597b3b4..f9b6ed8ad 100644 --- a/src/Cuemon.Extensions.Core/ObjectExtensions.cs +++ b/src/Cuemon.Extensions.Core/ObjectExtensions.cs @@ -65,7 +65,7 @@ public static int GetHashCode32(this IEnumerable convertibles) where T : I /// /// A sequence of objects implementing the interface. /// A 64-bit signed integer that is the hash code of . - public static long GetHashCode64(this IEnumerable convertibles) where T : struct, IConvertible + public static long GetHashCode64(this IEnumerable convertibles) where T : IConvertible { return Generate.HashCode64(convertibles.Cast()); } @@ -101,21 +101,14 @@ public static T Adjust(this T value, Func tweaker) /// /// Determines whether the specified source is a nullable . /// - /// The type of the of . - /// The source type to check for nullable . + /// The type of the of . + /// The source type to check for nullable . /// /// true if the specified source is nullable; otherwise, false. /// - public static bool IsNullable(this T source) { return false; } - - /// - /// Determines whether the specified source is a nullable . - /// - /// The type of the of . - /// The source type to check for nullable . - /// - /// true if the specified source is nullable; otherwise, false. - /// - public static bool IsNullable(this T? source) where T : struct { return true; } + public static bool IsNullable(this T _) + { + return typeof(T).IsNullable(); + } } } \ No newline at end of file From 4a7c2aded110c8c389c13861fb7ced9d8009bc6b Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 02:01:13 +0200 Subject: [PATCH 248/385] Minor tweaks. --- src/Cuemon.Extensions.Core/TypeExtensions.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Extensions.Core/TypeExtensions.cs b/src/Cuemon.Extensions.Core/TypeExtensions.cs index 427920d57..d9441e242 100644 --- a/src/Cuemon.Extensions.Core/TypeExtensions.cs +++ b/src/Cuemon.Extensions.Core/TypeExtensions.cs @@ -131,7 +131,8 @@ public static bool HasKeyValuePairImplementation(this Type type) public static bool IsNullable(this Type type) { Validator.ThrowIfNull(type, nameof(type)); - return Decorator.Enclose(type).IsNullable(); + if (!type.IsValueType) { return false; } + return Nullable.GetUnderlyingType(type) != null; } /// @@ -204,7 +205,7 @@ public static bool HasTypes(this Type type, params Type[] targets) } /// - /// Determines whether the specified contains one or more of the target types specified throughout this member's inheritance chain. + /// Determines whether the specified contains one or more of the target interfaces specified throughout this member's inheritance chain. /// /// The to extend. /// The target interface types to be matched against. From 0455b7fd15fd307f250bb8ea6083461cc2312341 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 02:02:06 +0200 Subject: [PATCH 249/385] Removed redundant method, aligned naming and changed hex from lower to upper. --- docfx/api/namespaces/Cuemon.Extensions.md | 24 ++++++++++++++++- src/Cuemon.Core/DelimitedString.cs | 2 +- src/Cuemon.Core/Wrapper.cs | 12 ++++++++- .../Cuemon.Extensions.Core.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 19 ++++++++++++++ .../StringExtensions.cs | 26 ++++--------------- .../Properties/PackageReleaseNotes.txt | 2 +- 7 files changed, 61 insertions(+), 26 deletions(-) create mode 100644 src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.md b/docfx/api/namespaces/Cuemon.Extensions.md index 755a5885d..107c3c025 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.md +++ b/docfx/api/namespaces/Cuemon.Extensions.md @@ -2,4 +2,26 @@ uid: Cuemon.Extensions summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions namespace contains extension methods that complements the Cuemon namespace while being an addition to the System namespace. + +Availability: NET Standard 2.0 + +Complements: [Cuemon namespace](https://docs.cuemon.net/api/dotnet/Cuemon.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Core)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Core)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Core) + +NuGet packages 📦\ +[Cuemon.Extensions.Core (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Core)\ +[Cuemon.Extensions.Core (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Core) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|Assembly|⬇️|`GetAssemblyVersion`, `GetFileVersion`, `GetProductVersion`, `IsDebugBuild`| +|MemberInfo|⬇️|`HasAttributes`| +|PropertyInfo|⬇️|`IsAutoProperty`| +|Type|⬇️|`GetEmbeddedResources`, `ToMethodBase`, `GetRuntimePropertiesExceptOf{T}`, `ToFullNameIncludingAssemblyName`| \ No newline at end of file diff --git a/src/Cuemon.Core/DelimitedString.cs b/src/Cuemon.Core/DelimitedString.cs index fc3875444..934f2c48b 100644 --- a/src/Cuemon.Core/DelimitedString.cs +++ b/src/Cuemon.Core/DelimitedString.cs @@ -9,7 +9,7 @@ namespace Cuemon { /// - /// Provides a set of static methods to break a delimited string into substrings. + /// Provides a set of static methods to convert a sequence into a delimited string and break a delimited string into substrings. /// public static class DelimitedString { diff --git a/src/Cuemon.Core/Wrapper.cs b/src/Cuemon.Core/Wrapper.cs index 4098c0f6e..0f33248f6 100644 --- a/src/Cuemon.Core/Wrapper.cs +++ b/src/Cuemon.Core/Wrapper.cs @@ -193,7 +193,17 @@ public TResult InstanceAs() /// public TResult InstanceAs(IFormatProvider provider) { - return (TResult)Decorator.Enclose(Instance).ChangeType(InstanceType, o => o.FormatProvider = provider); + var presult = Decorator.Enclose(Instance).ChangeType(InstanceType, o => o.FormatProvider = provider); + return Decorator.Enclose(presult).ChangeTypeOrDefault(); + } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + return Wrapper.ParseInstance(this); } #endregion } diff --git a/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj b/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj index 65bd033cd..7ca7419e7 100644 --- a/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj +++ b/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj @@ -9,7 +9,7 @@ Cuemon.Extensions Cuemon.Extensions.Core Cuemon.Extensions - The Cuemon.Extensions namespace contains extension methods and features related to the Cuemon namespace. + The Cuemon.Extensions namespace contains extension methods that complements the Cuemon namespace while being an addition to the System namespace. extension-methods extensions to-byte-array flatten to-encoded-string configure create-instance action-delegate options-pattern diff --git a/src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..27073ac7a --- /dev/null +++ b/src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt @@ -0,0 +1,19 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED ActionExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Action delegate: Configure{TOptions}, CreateInstance{T} +- ADDED ByteExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Byte struct: ToEncodedString, ToHexadecimalString, ToBinaryString, ToUrlEncodedBase64String, ToBase64String, TryDetectUnicodeEncoding +- ADDED CharExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Char struct: ToEnumerable, FromChars +- ADDED ConditionExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Condition class: HasDifference, HasDistinctDifference +- ADDED DateTimeExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the DateTime struct: ToUnixEpochTime, ToUtcKind, ToLocalKind, ToDefaultKind, IsWithinRange, IsTimeOfDayNight, IsTimeOfDayMorning, IsTimeOfDayForenoon, IsTimeOfDayAfternoon, IsTimeOfDayEvening, Floor, Ceiling, Round +- ADDED DoubleExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Double struct: FromUnixEpochTime, ToTimeSpan, Factorial, RoundOff +- ADDED ExceptionExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Exception class: Flatten +- ADDED IntegerExtensions class in the Cuemon.Extensions namespace that consist of extension methods for signed integers: Min, Max, IsPrime, IsCountableSequence, IsEven, IsOdd +- ADDED MappingExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Mapping class: Add +- ADDED ObjectExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Object class: UseWrapper{T}, As{T}, GetHashCode32{T}, GetHashCode64{T}, ToDelimitedString{T}, Adjust{T}, IsNullable{T} +- ADDED StringExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the String class: Difference, DistinctDifference, ToCharArray, ToByteArray, FromUrlEncodedBase64String, ToGuid, FromBinaryDigits, FromBase64, Join, ToCasing, ToUri, IsNullOrEmpty, IsNullOrWhiteSpace, IsEmailAddress, IsGuid, IsHex, IsNumeric, IsBase64, IsCountableSequence, SplitDelimited, Count, RemoveAll, ReplaceAll, JsEscape, JsUnescape, ContainsAny, ContainsAll, EqualsAny, StartsWith, TrimAll, IsSequenceOf{T}, FromHexadecimal, ToHexadecimal, ToEnum{TEnum}, ToTimeSpan, SubstringBefore, Chunk, SuffixWith, SuffixWithForwardingSlash, PrefixWith +- ADDED TimeSpanExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the TimeSpan struct: GetTotalNanoseconds, GetTotalMicroseconds, Floor, Ceiling, Round +- ADDED TypeExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Type class: ToFriendlyName, ToTypeCode, HasEqualityComparerImplementation, HasComparableImplementation, HasComparerImplementation, HasEnumerableImplementation, HasDictionaryImplementation, HasKeyValuePairImplementation, IsNullable, HasAnonymousCharacteristics, IsComplex, IsSimple, GetDefaultValue, HasTypes, HasInterfaces, HasAttributes +- ADDED ValidatorExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Validator class: HasDifference, NoDifference +  \ No newline at end of file diff --git a/src/Cuemon.Extensions.Core/StringExtensions.cs b/src/Cuemon.Extensions.Core/StringExtensions.cs index c474a9fc6..2e08a0273 100644 --- a/src/Cuemon.Extensions.Core/StringExtensions.cs +++ b/src/Cuemon.Extensions.Core/StringExtensions.cs @@ -21,26 +21,10 @@ public static class StringExtensions /// The value to compare with . /// >A that contains the set difference between and or if no difference. public static string Difference(this string first, string second) - { - return DifferenceCore(first, second); - } - - /// - /// Returns the distinct set difference between and or if no difference. - /// - /// The value where distinct characters that are not also in will be returned. - /// The value to distinctively compare with . - /// >A that contains the distinct set difference between and or if no difference. - public static string DistinctDifference(this string first, string second) - { - return DifferenceCore(first, second, true); - } - - private static string DifferenceCore(string first, string second, bool distinct = false) { if (first == null) { first = string.Empty; } if (second == null) { second = string.Empty; } - return distinct ? string.Concat(second.Distinct().Except(first.Distinct())) : string.Concat(second.Except(first)); + return string.Concat(second.Except(first)); } /// @@ -96,7 +80,7 @@ public static byte[] ToByteArray(this string input, Action setu /// has illegal base64 characters. /// /// - public static byte[] FromUrlEncodedBase64String(this string input) + public static byte[] FromUrlEncodedBase64(this string input) { return ParserFactory.FromUrlEncodedBase64().Parse(input); } @@ -117,7 +101,7 @@ public static byte[] FromUrlEncodedBase64String(this string input) /// The specified was not recognized to be a GUID. /// /// - public static Guid ToGuid(string input, Action setup = null) + public static Guid ToGuid(this string input, Action setup = null) { return ParserFactory.FromGuid().Parse(input, setup); } @@ -483,7 +467,7 @@ public static string JsEscape(this string value) { if (DoEscapeOrUnescape(character)) { - builder.AppendFormat(CultureInfo.InvariantCulture, character < byte.MaxValue ? "%{0:x2}" : "%u{0:x4}", (uint)character); + builder.AppendFormat(CultureInfo.InvariantCulture, character < byte.MaxValue ? "%{0:X2}" : "%u{0:X4}", (uint)character); } else { @@ -513,7 +497,7 @@ public static string JsUnescape(this string value) { if (DoEscapeOrUnescape(i)) { - builder.Replace(string.Format(CultureInfo.InvariantCulture, "%{0:x2}", i), Convert.ToChar(i).ToString()); + builder.Replace(string.Format(CultureInfo.InvariantCulture, "%{0:X2}", i), Convert.ToChar(i).ToString()); } } return builder.ToString(); diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt index a3dca2073..9db989ecd 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt @@ -17,5 +17,5 @@ Availability: NET Standard 2.0 - ADDED JData class in the Cuemon.Extensions.Newtonsoft.Json namespace that provides a factory based way to parse and extract values from various sources of JSON data. Compliant with RFC 7159 as it uses JsonTextReader behind the scene - ADDED JDataResultExtensions class in the Cuemon.Extensions.Newtonsoft.Json namespace that consist of extension methods for the JDataResult class: ExtractArrayValues, ExtractObjectValues - EXTENDED JsonSerializerSettingsExtensions class in the Cuemon.Extensions.Newtonsoft.Json namespace with one new extension method for the JsonSerializerSettings class: UseCamelCase -- ADDED ValidatorExtensions class in the Cuemon.Extensions.Newtonsoft.Json namespace that consist of extension methods for the Validator class: IfNotValidJsonDocument +- ADDED ValidatorExtensions class in the Cuemon.Extensions.Newtonsoft.Json namespace that consist of extension methods for the Validator class: InvalidJsonDocument   \ No newline at end of file From 3ae66730b0cc6cdb5476aadce79d0c0aff0bfa67 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 02:23:39 +0200 Subject: [PATCH 250/385] Updated package release notes and DocFx namespace descriptions. --- .../Cuemon.Extensions.Newtonsoft.Json.md | 2 +- docfx/api/namespaces/Cuemon.Extensions.md | 18 ++++++++++++++---- .../Properties/PackageReleaseNotes.txt | 4 ++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md index 7968854b1..246d1380e 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md @@ -25,4 +25,4 @@ NuGet packages 📦\ |JsonReader|⬇️|`ToHierarchy`| |JsonSerializerSettings|⬇️|`ApplyToDefaultSettings`, `UseCamelCase`| |JsonWriter|⬇️|`WriteObject`, `WritePropertyName`| -|Validator|⬇️|`IfNotValidJsonDocument`| \ No newline at end of file +|Validator|⬇️|`InvalidJsonDocument`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.md b/docfx/api/namespaces/Cuemon.Extensions.md index 107c3c025..3952a6884 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.md +++ b/docfx/api/namespaces/Cuemon.Extensions.md @@ -21,7 +21,17 @@ NuGet packages 📦\ |Type|Ext|Methods| |--:|:-:|---| -|Assembly|⬇️|`GetAssemblyVersion`, `GetFileVersion`, `GetProductVersion`, `IsDebugBuild`| -|MemberInfo|⬇️|`HasAttributes`| -|PropertyInfo|⬇️|`IsAutoProperty`| -|Type|⬇️|`GetEmbeddedResources`, `ToMethodBase`, `GetRuntimePropertiesExceptOf{T}`, `ToFullNameIncludingAssemblyName`| \ No newline at end of file +|Action|⬇️|`Configure{TOptions}`, `CreateInstance{T}`| +|Byte|⬇️|`ToEncodedString`, `ToHexadecimalString`, `ToBinaryString`, `ToUrlEncodedBase64String`, `ToBase64String`, `TryDetectUnicodeEncoding`| +|Char|⬇️|`ToEnumerable`, `FromChars`| +|Condition|⬇️|`HasDifference`| +|DateTime|⬇️|`ToUnixEpochTime`, `ToUtcKind`, `ToLocalKind`, `ToDefaultKind`, `IsWithinRange`, `IsTimeOfDayNight`, `IsTimeOfDayMorning`, `IsTimeOfDayForenoon`, `IsTimeOfDayAfternoon`, `IsTimeOfDayEvening`, `Floor`, `Ceiling`, `Round`| +|Double|⬇️|`FromUnixEpochTime`, `ToTimeSpan`, `Factorial`, `RoundOff`| +|Exception|⬇️|`Flatten`| +|Int*|⬇️|`Min`, `Max`, `IsPrime`, `IsCountableSequence`, `IsEven`, `IsOdd`| +|Mapping|⬇️|`Add`| +|Object|⬇️|`UseWrapper{T}`, `As{T}`, `GetHashCode32{T}`, `GetHashCode64{T}`, `ToDelimitedString{T}`, `Adjust{T}`, `IsNullable{T}`| +|String|⬇️|`Difference`, `ToCharArray`, `ToByteArray`, `FromUrlEncodedBase64`, `ToGuid`, `FromBinaryDigits`, `FromBase64`, `Join`, `ToCasing`, `ToUri`, `IsNullOrEmpty`, `IsNullOrWhiteSpace`, `IsEmailAddress`, `IsGuid`, `IsHex`, `IsNumeric`, `IsBase64`, `IsCountableSequence`, `SplitDelimited`, `Count`, `RemoveAll`, `ReplaceAll`, `JsEscape`, `JsUnescape`, `ContainsAny`, `ContainsAll`, `EqualsAny`, `StartsWith`, `TrimAll`, `IsSequenceOf{T}`, `FromHexadecimal`, `ToHexadecimal`, `ToEnum{TEnum}`, `ToTimeSpan`, `SubstringBefore`, `Chunk`, `SuffixWith`, `SuffixWithForwardingSlash`, `PrefixWith`| +|TimeSpan|⬇️|`GetTotalNanoseconds`, `GetTotalMicroseconds`, `Floor`, `Ceiling`, `Round`| +|Type|⬇️|`ToFriendlyName`, `ToTypeCode`, `HasEqualityComparerImplementation`, `HasComparableImplementation`, `HasComparerImplementation`, `HasEnumerableImplementation`, `HasDictionaryImplementation`, `HasKeyValuePairImplementation`, `IsNullable`, `HasAnonymousCharacteristics`, `IsComplex`, `IsSimple`, `GetDefaultValue`, `HasTypes`, `HasInterfaces`, `HasAttributes`| +|Validator|⬇️|`HasDifference`, `NoDifference`| \ No newline at end of file diff --git a/src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt index 27073ac7a..57c9db934 100644 --- a/src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt @@ -5,14 +5,14 @@ Availability: NET Standard 2.0 - ADDED ActionExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Action delegate: Configure{TOptions}, CreateInstance{T} - ADDED ByteExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Byte struct: ToEncodedString, ToHexadecimalString, ToBinaryString, ToUrlEncodedBase64String, ToBase64String, TryDetectUnicodeEncoding - ADDED CharExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Char struct: ToEnumerable, FromChars -- ADDED ConditionExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Condition class: HasDifference, HasDistinctDifference +- ADDED ConditionExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Condition class: HasDifference - ADDED DateTimeExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the DateTime struct: ToUnixEpochTime, ToUtcKind, ToLocalKind, ToDefaultKind, IsWithinRange, IsTimeOfDayNight, IsTimeOfDayMorning, IsTimeOfDayForenoon, IsTimeOfDayAfternoon, IsTimeOfDayEvening, Floor, Ceiling, Round - ADDED DoubleExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Double struct: FromUnixEpochTime, ToTimeSpan, Factorial, RoundOff - ADDED ExceptionExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Exception class: Flatten - ADDED IntegerExtensions class in the Cuemon.Extensions namespace that consist of extension methods for signed integers: Min, Max, IsPrime, IsCountableSequence, IsEven, IsOdd - ADDED MappingExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Mapping class: Add - ADDED ObjectExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Object class: UseWrapper{T}, As{T}, GetHashCode32{T}, GetHashCode64{T}, ToDelimitedString{T}, Adjust{T}, IsNullable{T} -- ADDED StringExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the String class: Difference, DistinctDifference, ToCharArray, ToByteArray, FromUrlEncodedBase64String, ToGuid, FromBinaryDigits, FromBase64, Join, ToCasing, ToUri, IsNullOrEmpty, IsNullOrWhiteSpace, IsEmailAddress, IsGuid, IsHex, IsNumeric, IsBase64, IsCountableSequence, SplitDelimited, Count, RemoveAll, ReplaceAll, JsEscape, JsUnescape, ContainsAny, ContainsAll, EqualsAny, StartsWith, TrimAll, IsSequenceOf{T}, FromHexadecimal, ToHexadecimal, ToEnum{TEnum}, ToTimeSpan, SubstringBefore, Chunk, SuffixWith, SuffixWithForwardingSlash, PrefixWith +- ADDED StringExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the String class: Difference, ToCharArray, ToByteArray, FromUrlEncodedBase64, ToGuid, FromBinaryDigits, FromBase64, Join, ToCasing, ToUri, IsNullOrEmpty, IsNullOrWhiteSpace, IsEmailAddress, IsGuid, IsHex, IsNumeric, IsBase64, IsCountableSequence, SplitDelimited, Count, RemoveAll, ReplaceAll, JsEscape, JsUnescape, ContainsAny, ContainsAll, EqualsAny, StartsWith, TrimAll, IsSequenceOf{T}, FromHexadecimal, ToHexadecimal, ToEnum{TEnum}, ToTimeSpan, SubstringBefore, Chunk, SuffixWith, SuffixWithForwardingSlash, PrefixWith - ADDED TimeSpanExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the TimeSpan struct: GetTotalNanoseconds, GetTotalMicroseconds, Floor, Ceiling, Round - ADDED TypeExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Type class: ToFriendlyName, ToTypeCode, HasEqualityComparerImplementation, HasComparableImplementation, HasComparerImplementation, HasEnumerableImplementation, HasDictionaryImplementation, HasKeyValuePairImplementation, IsNullable, HasAnonymousCharacteristics, IsComplex, IsSimple, GetDefaultValue, HasTypes, HasInterfaces, HasAttributes - ADDED ValidatorExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Validator class: HasDifference, NoDifference From c33d3aed9f2ae54f775c5db26c0d52ccc5b8353b Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 02:30:25 +0200 Subject: [PATCH 251/385] Updated package description, release notes and DocFx namespace description. --- ...emon.Extensions.Collections.Specialized.md | 22 ++++++++++++++++++- ....Extensions.Collections.Specialized.csproj | 2 +- .../DictionaryExtensions.cs | 2 +- .../Properties/PackageReleaseNotes.txt | 7 ++++++ 4 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 src/Cuemon.Extensions.Collections.Specialized/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md b/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md index 1a916f2b9..9cfa90991 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md @@ -2,4 +2,24 @@ uid: Cuemon.Extensions.Collections.Specialized summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon namespace while being an addition to the System.Collections.Specialized namespace. + +Availability: NET Standard 2.0 + +Complements: [Cuemon.Collections.Specialized namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Collections.Specialized.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Collections.Specialized)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Collections.Specialized)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Collections.Specialized) + +NuGet packages 📦\ +[Cuemon.Extensions.Collections.Specialized (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Collections.Specialized)\ +[Cuemon.Extensions.Collections.Specialized (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Collections.Specialized) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IDictionary{string, string[]}|⬇️|`ToNameValueCollection`| +|NameValueCollection|⬇️|`ContainsKey`, `ToDictionary`| \ No newline at end of file diff --git a/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj b/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj index 2459267b6..cb380eb94 100644 --- a/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj +++ b/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Collections.Specialized Cuemon.Extensions.Collections.Specialized - The Cuemon.Extensions.Collections.Specialized namespace contains extension methods and features related to the Cuemon.Collections.Specialized namespace. + The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon namespace while being an addition to the System.Collections.Specialized namespace. extension-methods extensions to-name-value-collection to-dictionary diff --git a/src/Cuemon.Extensions.Collections.Specialized/DictionaryExtensions.cs b/src/Cuemon.Extensions.Collections.Specialized/DictionaryExtensions.cs index fbd81622b..a6e143006 100644 --- a/src/Cuemon.Extensions.Collections.Specialized/DictionaryExtensions.cs +++ b/src/Cuemon.Extensions.Collections.Specialized/DictionaryExtensions.cs @@ -13,7 +13,7 @@ public static class DictionaryExtensions /// /// Creates a from the specified . /// - /// An to convert into an equivalent. + /// An to extend. /// The which may be configured. /// A that is equivalent to the specified . /// diff --git a/src/Cuemon.Extensions.Collections.Specialized/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Collections.Specialized/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..f47c06e8f --- /dev/null +++ b/src/Cuemon.Extensions.Collections.Specialized/Properties/PackageReleaseNotes.txt @@ -0,0 +1,7 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED DictionaryExtensions class in the Cuemon.Extensions.Collections.Specialized namespace that consist of extension methods for the IDictionary{string, string[]} interface: ToNameValueCollection +- ADDED NameValueCollectionExtensions class in the Cuemon.Extensions.Collections.Specialized namespace that consist of extension methods for the NameValueCollection class: ContainsKey, ToDictionary +  \ No newline at end of file From ae2adc3059b4b7c8940c62a29cb6264c5ccef7a5 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 02:49:24 +0200 Subject: [PATCH 252/385] Minor adjustments. --- .../CollectionExtensions.cs | 6 +- .../DictionaryExtensions.cs | 14 +-- .../EnumerableExtensions.cs | 108 +++++++++--------- .../ListExtensions.cs | 54 ++++----- 4 files changed, 91 insertions(+), 91 deletions(-) diff --git a/src/Cuemon.Extensions.Collections.Generic/CollectionExtensions.cs b/src/Cuemon.Extensions.Collections.Generic/CollectionExtensions.cs index 76d641297..58c31d2ea 100644 --- a/src/Cuemon.Extensions.Collections.Generic/CollectionExtensions.cs +++ b/src/Cuemon.Extensions.Collections.Generic/CollectionExtensions.cs @@ -12,7 +12,7 @@ public static class CollectionExtensions /// Extends the specified to support iterating in partitions. /// /// The type of elements in the . - /// The collection to extend. + /// The to extend. /// The size of the partitions. /// An instance of . public static PartitionerCollection ToPartitioner(this ICollection collection, int partitionSize = 128) @@ -24,7 +24,7 @@ public static PartitionerCollection ToPartitioner(this ICollection coll /// Adds the elements of the specified to the . /// /// The type of elements in the . - /// The collection to extend. + /// The to extend. /// The sequence of elements that should be added to . public static void AddRange(this ICollection collection, params T[] source) { @@ -35,7 +35,7 @@ public static void AddRange(this ICollection collection, params T[] source /// Adds the elements of the specified to the . /// /// The type of elements in the . - /// The collection to extend. + /// The to extend. /// The sequence of elements that should be added to . public static void AddRange(this ICollection collection, IEnumerable source) { diff --git a/src/Cuemon.Extensions.Collections.Generic/DictionaryExtensions.cs b/src/Cuemon.Extensions.Collections.Generic/DictionaryExtensions.cs index f4b7cf10b..2cbb337ef 100644 --- a/src/Cuemon.Extensions.Collections.Generic/DictionaryExtensions.cs +++ b/src/Cuemon.Extensions.Collections.Generic/DictionaryExtensions.cs @@ -14,7 +14,7 @@ public static class DictionaryExtensions /// /// The type of the keys in the . /// The type of the values in the . - /// The dictionary to extend. + /// The to extend. /// The key of the value to get. /// Either the value associated with the specified or default() when the key does not exists. /// @@ -32,7 +32,7 @@ public static TValue GetValueOrDefault(this IDictionary /// The type of the keys in the . /// The type of the values in the . - /// The dictionary to extend. + /// The to extend. /// The key of the value to get. /// The function delegate that will provide a default value when the does not exists in the . /// Either the value associated with the specified or a default value through when the key does not exists. @@ -52,7 +52,7 @@ public static TValue GetValueOrDefault(this IDictionary /// The type of the keys in the . /// The type of the values in the . - /// The dictionary to extend. + /// The to extend. /// The key of the value to get. /// The function delegate that will resolve an alternate key from the specified . /// When this method returns, contains the value associated with the specified or the alternate key resolved from , if the key is found; otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized. @@ -71,7 +71,7 @@ public static bool TryGetValueOrFallback(this IDictionary /// The of the key in the resulting . /// The of the value in the resulting . - /// An to convert into a equivalent sequence. + /// The to extend. /// A equivalent sequence of . /// /// is null. @@ -85,7 +85,7 @@ public static IEnumerable> ToEnumerable /// /// Attempts to add the specified and to the . /// - /// The dictionary to extend. + /// The to extend. /// The key of the element to add. /// The value of the element to add. /// The function delegate that specifies the condition for adding the element. @@ -104,7 +104,7 @@ public static bool TryAdd(this IDictionary dictionar /// /// Attempts to add the specified and to the . /// - /// The dictionary to extend. + /// The to extend. /// The key of the element to add. /// The value of the element to add. /// true if the key/value pair was added to the enclosed of the successfully; otherwise, false. @@ -121,7 +121,7 @@ public static bool TryAdd(this IDictionary dictionar /// /// Attempts to add or update an existing element with the provided to the with the specified . /// - /// The dictionary to extend. + /// The to extend. /// The key of the element to add or update. /// The value of the element to add or update. /// diff --git a/src/Cuemon.Extensions.Collections.Generic/EnumerableExtensions.cs b/src/Cuemon.Extensions.Collections.Generic/EnumerableExtensions.cs index 6d62d1a11..8eeb217b0 100644 --- a/src/Cuemon.Extensions.Collections.Generic/EnumerableExtensions.cs +++ b/src/Cuemon.Extensions.Collections.Generic/EnumerableExtensions.cs @@ -13,8 +13,8 @@ public static class EnumerableExtensions /// /// Returns a chunked sequence with a maximum of the specified . Default is 128. /// - /// The type of the elements of . - /// An to chunk into smaller slices for a batch run or similar. + /// The type of the elements of . + /// An to extend. /// The amount of elements to process at a time. /// An that contains no more than the specified of elements from the sequence. /// @@ -24,20 +24,20 @@ public static class EnumerableExtensions /// is less or equal to 0. /// /// The original is reduced equivalent to the number of elements in the returned sequence. - public static PartitionerEnumerable Chunk(this IEnumerable source, int size = 128) + public static PartitionerEnumerable Chunk(this IEnumerable source, int size = 128) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfLowerThanOrEqual(0, size, nameof(size)); - return new PartitionerEnumerable(source, size); + return new PartitionerEnumerable(source, size); } /// /// Shuffles the specified like a deck of cards. /// - /// The elements to be shuffled in the randomization process. - /// A sequence of with the shuffled . + /// An to extend. + /// A sequence of with the shuffled . /// Fisher–Yates shuffle: https://en.wikipedia.org/wiki/Fisher–Yates_shuffle - public static IEnumerable Shuffle(this IEnumerable source) + public static IEnumerable Shuffle(this IEnumerable source) { return source.Shuffle(Generate.RandomNumber); } @@ -45,11 +45,11 @@ public static IEnumerable Shuffle(this IEnumerable so /// /// Shuffles the specified like a deck of cards. /// - /// The elements to be shuffled in the randomization process. + /// An to extend. /// The function delegate that will handle the randomization of . - /// A sequence of with the shuffled . + /// A sequence of with the shuffled . /// Fisher–Yates shuffle: https://en.wikipedia.org/wiki/Fisher–Yates_shuffle - public static IEnumerable Shuffle(this IEnumerable source, Func randomizer) + public static IEnumerable Shuffle(this IEnumerable source, Func randomizer) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(randomizer, nameof(randomizer)); @@ -68,22 +68,22 @@ public static IEnumerable Shuffle(this IEnumerable so /// /// Returns ascending sorted elements from a sequence by using the default comparer to compare values. /// - /// The type of the elements of . - /// A sequence of values to order. + /// The type of the elements of . + /// An to extend. /// An that contains ascending sorted elements from the source sequence. - public static IEnumerable OrderBy(this IEnumerable source) + public static IEnumerable OrderBy(this IEnumerable source) { - return source.OrderBy(Comparer.Default); + return source.OrderBy(Comparer.Default); } /// /// Returns ascending sorted elements from a sequence by using a specified to compare values. /// - /// The type of the elements of . - /// A sequence of values to order. + /// The type of the elements of . + /// An to extend. /// An to compare values. /// An that contains ascending sorted elements from the source sequence. - public static IEnumerable OrderBy(this IEnumerable source, IComparer comparer) + public static IEnumerable OrderBy(this IEnumerable source, IComparer comparer) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(comparer, nameof(comparer)); @@ -93,22 +93,22 @@ public static IEnumerable OrderBy(this IEnumerable so /// /// Returns descending sorted elements from a sequence by using the default comparer to compare values. /// - /// The type of the elements of . - /// A sequence of values to order. + /// The type of the elements of . + /// An to extend. /// An that contains descending sorted elements from the source sequence. - public static IEnumerable OrderByDescending(this IEnumerable source) + public static IEnumerable OrderByDescending(this IEnumerable source) { - return source.OrderByDescending(Comparer.Default); + return source.OrderByDescending(Comparer.Default); } /// /// Returns descending sorted elements from a sequence by using a specified to compare values. /// - /// The type of the elements of . - /// A sequence of values to order. + /// The type of the elements of . + /// An to extend. /// An to compare values. /// An that contains descending sorted elements from the source sequence. - public static IEnumerable OrderByDescending(this IEnumerable source, IComparer comparer) + public static IEnumerable OrderByDescending(this IEnumerable source, IComparer comparer) { Validator.ThrowIfNull(source, nameof(source)); Validator.ThrowIfNull(comparer, nameof(comparer)); @@ -118,13 +118,13 @@ public static IEnumerable OrderByDescending(this IEnumerable /// Returns a random element of a sequence of elements, or a default value if no element is found. /// - /// The type of the elements of . - /// The to return a random element of. + /// The type of the elements of . + /// An to extend. /// default if is empty; otherwise, a random element of . - public static TSource RandomOrDefault(this IEnumerable source) + public static T RandomOrDefault(this IEnumerable source) { Validator.ThrowIfNull(source, nameof(source)); - var collection = source as ICollection ?? new List(source); + var collection = source as ICollection ?? new List(source); return collection.Count == 0 ? default : collection.ElementAt(Generate.RandomNumber(collection.Count)); } @@ -144,7 +144,7 @@ public static IEnumerable Yield(this T value) /// /// The type of keys in the . /// The type of values in the . - /// The sequence to create a from. + /// An to extend. /// A that is equivalent to the specified sequence. /// /// is null. @@ -162,7 +162,7 @@ public static IDictionary ToDictionary(this IEnumera /// /// The type of keys in the . /// The type of values in the . - /// The sequence to create a from. + /// An to extend. /// The implementation to use when comparing keys. /// A that is equivalent to the specified sequence. /// @@ -187,23 +187,23 @@ public static IDictionary ToDictionary(this IEnumera /// /// Extends the specified to support iterating in partitions. /// - /// The type of elements in the . - /// The sequence to extend. + /// The type of elements in the . + /// An to extend. /// The size of the partitions. /// An instance of . - public static PartitionerEnumerable ToPartitioner(this IEnumerable source, int partitionSize = 128) + public static PartitionerEnumerable ToPartitioner(this IEnumerable source, int partitionSize = 128) { - return new PartitionerEnumerable(source, partitionSize); + return new PartitionerEnumerable(source, partitionSize); } /// /// Converts the specified to a paged data sequence. /// - /// The type of the elements of . - /// The source of the sequence to make pageable. + /// The type of the elements of . + /// An to extend. /// An instance of . /// The starting page is set to 1 and the page size is determined by . - public static PagedCollection ToPagedCollection(this IEnumerable source) + public static PagedCollection ToPagedCollection(this IEnumerable source) { return ToPagedCollection(source, 1); } @@ -211,12 +211,12 @@ public static PagedCollection ToPagedCollection(this IEnumerab /// /// Converts the specified to a paged data sequence initialized with starting . /// - /// The type of the elements of . - /// The source of the sequence to make pageable. + /// The type of the elements of . + /// An to extend. /// The page number to start with. /// An instance of . /// The page size is determined by . - public static PagedCollection ToPagedCollection(this IEnumerable source, int pageNumber) + public static PagedCollection ToPagedCollection(this IEnumerable source, int pageNumber) { return ToPagedCollection(source, pageNumber, PagedSettings.DefaultPageSize); } @@ -224,12 +224,12 @@ public static PagedCollection ToPagedCollection(this IEnumerab /// /// Converts the specified to a paged data sequence initialized with starting and . /// - /// The type of the elements of . - /// The source of the sequence to make pageable. + /// The type of the elements of . + /// An to extend. /// The page number to start with. /// The number of elements a page can contain. /// An instance of . - public static PagedCollection ToPagedCollection(this IEnumerable source, int pageNumber, int pageSize) + public static PagedCollection ToPagedCollection(this IEnumerable source, int pageNumber, int pageSize) { return ToPagedCollection(source, new PagedSettings() { PageNumber = pageNumber, PageSize = pageSize }); } @@ -237,25 +237,25 @@ public static PagedCollection ToPagedCollection(this IEnumerab /// /// Converts the specified to a paged data sequence initialized with . /// - /// The type of the elements of . - /// The source of the sequence to make pageable. + /// The type of the elements of . + /// An to extend. /// The settings that specifies the conditions of the converted . /// An instance of initialized with . - public static PagedCollection ToPagedCollection(this IEnumerable source, PagedSettings settings) + public static PagedCollection ToPagedCollection(this IEnumerable source, PagedSettings settings) { - return new PagedCollection(source, settings); + return new PagedCollection(source, settings); } /// /// Converts the specified to a paged data sequence initialized with starting and . /// - /// The type of the elements of . - /// The source of the sequence to make pageable. + /// The type of the elements of . + /// An to extend. /// The page number to start with. /// The number of elements a page can contain. /// The total number of elements in the sequence. /// An instance of . - public static PagedCollection ToPagedCollection(this IEnumerable source, int pageNumber, int pageSize, int totalElementCount) + public static PagedCollection ToPagedCollection(this IEnumerable source, int pageNumber, int pageSize, int totalElementCount) { return ToPagedCollection(source, new PagedSettings() { PageNumber = pageNumber, PageSize = pageSize }, totalElementCount); } @@ -263,14 +263,14 @@ public static PagedCollection ToPagedCollection(this IEnumerab /// /// Converts the specified to a paged data sequence initialized with . /// - /// The type of the elements of . - /// The source of the sequence to make pageable. + /// The type of the elements of . + /// An to extend. /// The settings that specifies the conditions of the converted . /// The total number of elements in the sequence. /// An instance of initialized with . - public static PagedCollection ToPagedCollection(this IEnumerable source, PagedSettings settings, int totalElementCount) + public static PagedCollection ToPagedCollection(this IEnumerable source, PagedSettings settings, int totalElementCount) { - return new PagedCollection(source, settings, totalElementCount); + return new PagedCollection(source, settings, totalElementCount); } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Collections.Generic/ListExtensions.cs b/src/Cuemon.Extensions.Collections.Generic/ListExtensions.cs index 57f850791..c1d611813 100644 --- a/src/Cuemon.Extensions.Collections.Generic/ListExtensions.cs +++ b/src/Cuemon.Extensions.Collections.Generic/ListExtensions.cs @@ -12,7 +12,7 @@ public static class ListExtensions /// Removes the first occurrence of a specific object from the . /// /// The type of elements in the . - /// The extended list. + /// The to extend. /// The function delegate that defines the conditions of the element to remove. /// true if item was successfully removed from the , false otherwise. public static bool Remove(this IList list, Func predicate) @@ -29,64 +29,64 @@ public static bool Remove(this IList list, Func predicate) } /// - /// Determines whether the of the is within the range of the . + /// Determines whether the of the is within the range of the . /// - /// The type of elements in the . - /// The elements of the . + /// The type of elements in the . + /// The to extend. /// The index to find. - /// true if the specified is within the range of the ; otherwise, false. + /// true if the specified is within the range of the ; otherwise, false. /// - /// is null. + /// is null. /// - public static bool HasIndex(this IList elements, int index) + public static bool HasIndex(this IList list, int index) { - Validator.ThrowIfNull(elements, nameof(elements)); - return ((elements.Count - 1) >= index); + Validator.ThrowIfNull(list, nameof(list)); + return ((list.Count - 1) >= index); } /// - /// Returns the next element of relative to , or the last element of if is equal or greater than . + /// Returns the next element of relative to , or the last element of if is equal or greater than . /// - /// The type of elements in the . - /// The elements, relative to , to return the next element of. + /// The type of elements in the . + /// The to extend. /// The index of which to advance to the next element from. /// - /// is null. + /// is null. /// /// /// is less than 0. /// - /// default(TSource) if is equal or greater than ; otherwise the next element of relative to . - public static TSource Next(this IList elements, int index) + /// default(TSource) if is equal or greater than ; otherwise the next element of relative to . + public static T Next(this IList list, int index) { - Validator.ThrowIfNull(elements, nameof(elements)); + Validator.ThrowIfNull(list, nameof(list)); Validator.ThrowIfLowerThan(index, 0, nameof(index)); var nextIndex = index + 1; - if (nextIndex >= elements.Count) { return default; } - return elements[nextIndex]; + if (nextIndex >= list.Count) { return default; } + return list[nextIndex]; } /// - /// Returns the previous element of relative to , or the first or last element of if is equal, greater or lower than . + /// Returns the previous element of relative to , or the first or last element of if is equal, greater or lower than . /// - /// The type of elements in the . - /// The elements, relative to , to return the previous element of. + /// The type of elements in the . + /// The to extend. /// The index of which to advance to the previous element from. /// - /// is null. + /// is null. /// /// /// is less than 0. /// - /// default(TSource) if is equal, greater or lower than ; otherwise the previous element of relative to . - public static TSource Previous(this IList elements, int index) + /// default(TSource) if is equal, greater or lower than ; otherwise the previous element of relative to . + public static T Previous(this IList list, int index) { - Validator.ThrowIfNull(elements, nameof(elements)); + Validator.ThrowIfNull(list, nameof(list)); Validator.ThrowIfLowerThan(index, 0, nameof(index)); var previousIndex = index - 1; if (previousIndex < 0) { return default; } - if (previousIndex >= elements.Count) { return default; } - return elements[previousIndex]; + if (previousIndex >= list.Count) { return default; } + return list[previousIndex]; } } } \ No newline at end of file From ab611865bb5cc2dd078be8be47be9fcbc4c6719a Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 02:49:56 +0200 Subject: [PATCH 253/385] Updated package description, release notes and DocFx namespace description. --- .../Cuemon.Extensions.Collections.Generic.md | 24 ++++++++++++++++++- ...emon.Extensions.Collections.Specialized.md | 2 +- ...emon.Extensions.Collections.Generic.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 9 +++++++ ....Extensions.Collections.Specialized.csproj | 2 +- 5 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 src/Cuemon.Extensions.Collections.Generic/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md b/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md index 83f02ceec..af31c09a4 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md @@ -2,4 +2,26 @@ uid: Cuemon.Extensions.Collections.Generic summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon.Extensions.Collections.Generic namespace while being an addition to the System.Collections.Specialized namespace. + +Availability: NET Standard 2.0 + +Complements: [Cuemon.Collections.Specialized namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Collections.Generic.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Collections.Generic)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Collections.Generic)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Collections.Generic) + +NuGet packages 📦\ +[Cuemon.Extensions.Collections.Generic (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Collections.Generic)\ +[Cuemon.Extensions.Collections.Generic (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Collections.Generic) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|ICollection{T}|⬇️|`ToPartitioner{T}`, `AddRange{T}`| +|IDictionary{TKey, TValue}|⬇️|`GetValueOrDefault{TKey, TValue}`, `TryGetValueOrFallback{TKey, TValue}`, `ToEnumerable{TKey, TValue}`, `TryAdd{TKey, TValue}`, `TryAddOrUpdate{TKey, TValue}`| +|IEnumerable{T}|⬇️|`Chunk{T}`, `Shuffle{T}`, `OrderBy{T}`, `OrderByDescending{T}`, `RandomOrDefault{T}`, `Yield{T}`, `ToDictionary{TKey, TValue}`, `ToPartitioner{T}`, `ToPagedCollection{T}`| +|IList{T}|⬇️|`Remove{T}`, `HasIndex{T}`, `Next{T}`, `Previous{T}`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md b/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md index 9cfa90991..181c1aa3c 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md @@ -2,7 +2,7 @@ uid: Cuemon.Extensions.Collections.Specialized summary: *content --- -The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon namespace while being an addition to the System.Collections.Specialized namespace. +The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon.Extensions.Collections.Specialized namespace while being an addition to the System.Collections.Specialized namespace. Availability: NET Standard 2.0 diff --git a/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj b/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj index bc1b93677..00957f8d4 100644 --- a/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj +++ b/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Collections.Generic Cuemon.Extensions.Collections.Generic - The Cuemon.Extensions.Collections.Generic namespace contains extension methods and features related to the System.Collections.Generic namespace. + The Cuemon.Extensions.Collections.Generic namespace contains extension methods that complements the Cuemon.Collections.Generic namespace while being an addition to the System.Collections.Generic namespace. extension-methods extensions to-partitioner chunk shuffle random-or-default yield diff --git a/src/Cuemon.Extensions.Collections.Generic/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Collections.Generic/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..3880e193e --- /dev/null +++ b/src/Cuemon.Extensions.Collections.Generic/Properties/PackageReleaseNotes.txt @@ -0,0 +1,9 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# New Features +- ADDED CollectionExtensions class in the Cuemon.Extensions.Collections.Generic namespace that consist of extension methods for the ICollection{T} interface: ToPartitioner{T}, AddRange{T} +- ADDED DictionaryExtensions class in the Cuemon.Extensions.Collections.Generic namespace that consist of extension methods for the IDictionary{TKey, TValue} interface: GetValueOrDefault{TKey, TValue}, TryGetValueOrFallback{TKey, TValue}, ToEnumerable{TKey, TValue}, TryAdd{TKey, TValue}, TryAddOrUpdate{TKey, TValue} +- ADDED EnumerableExtensions class in the Cuemon.Extensions.Collections.Generic namespace that consist of extension methods for the IEnumerable{T} interface: Chunk{T}, Shuffle{T}, OrderBy{T}, OrderByDescending{T}, RandomOrDefault{T}, Yield{T}, ToDictionary{TKey, TValue}, ToPartitioner{T}, ToPagedCollection{T} +- ADDED ListExtensions class in the Cuemon.Extensions.Collections.Generic namespace that consist of extension methods for the IList{T} interface: Remove{T}, HasIndex{T}, Next{T}, Previous{T} +  \ No newline at end of file diff --git a/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj b/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj index cb380eb94..0d2a06aea 100644 --- a/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj +++ b/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Collections.Specialized Cuemon.Extensions.Collections.Specialized - The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon namespace while being an addition to the System.Collections.Specialized namespace. + The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon.Collections.Specialized namespace while being an addition to the System.Collections.Specialized namespace. extension-methods extensions to-name-value-collection to-dictionary From 99a763154c495cb78a58ad91efa5b0bd7f1fa82d Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 17:16:09 +0200 Subject: [PATCH 254/385] Updated package description, release notes and DocFx namespace description. --- ...spNetCore.Mvc.Formatters.Xml.Converters.md | 21 +++++++++++++++++- ...xtensions.AspNetCore.Mvc.Formatters.Xml.md | 22 ++++++++++++++++++- .../Cuemon.Extensions.Collections.Generic.md | 2 +- ...emon.Extensions.Collections.Specialized.md | 2 +- ...sions.AspNetCore.Mvc.Formatters.Xml.csproj | 4 ++-- .../Properties/PackageReleaseNotes.txt | 10 +++++++++ 6 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md index 61989495a..0dd38103b 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters namespace contains extension methods that complements the Cuemon.Extensions.Xml.Converters namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.Extensions.Xml.Converters namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml)\ +[Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|XmlConverter|⬇️|`AddHttpExceptionDescriptorConverter`, `AddStringValuesConverter`, `AddHeaderDictionaryConverter`, `AddQueryCollectionConverter`, `AddFormCollectionConverter`, `AddCookieCollectionConverter`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md index 52c5a30d1..cd64f596c 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md @@ -2,4 +2,24 @@ uid: Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml namespace contains both types and extension methods that complements the Cuemon.Extensions.Xml namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides XML formatters for ASP.NET Core that offers same flexibility as the one provided by the JSON equivalent from Newtonsoft. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.Extensions.Xml namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Xml.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml)\ +[Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IMvcBuilder|⬇️|`AddXmlSerializationFormatters`, `AddXmlFormatterOptions`| +|IMvcCoreBuilder|⬇️|`AddXmlSerializationFormatters`, `AddXmlFormatterOptions`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md b/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md index af31c09a4..0d969530c 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md @@ -2,7 +2,7 @@ uid: Cuemon.Extensions.Collections.Generic summary: *content --- -The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon.Extensions.Collections.Generic namespace while being an addition to the System.Collections.Specialized namespace. +The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon.Collections.Generic namespace while being an addition to the System.Collections.Specialized namespace. Availability: NET Standard 2.0 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md b/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md index 181c1aa3c..508fcf733 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md @@ -2,7 +2,7 @@ uid: Cuemon.Extensions.Collections.Specialized summary: *content --- -The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon.Extensions.Collections.Specialized namespace while being an addition to the System.Collections.Specialized namespace. +The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon.Collections.Specialized namespace while being an addition to the System.Collections.Specialized namespace. Availability: NET Standard 2.0 diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj index b79b1d899..2996389ac 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj @@ -8,8 +8,8 @@ Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml - The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml namespace provides extension methods and XML formatters for ASP.NET Core MVC. - extension-methods extensions xml-converters + The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml namespace contains both types and extension methods that complements the Cuemon.Extensions.Xml namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides XML formatters for ASP.NET Core that offers same flexibility as the one provided by the JSON equivalent from Newtonsoft. + extension-methods extensions xml-converters add-xml-serialization-formatters add-xml-formatter-options diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..02634bd3e --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Properties/PackageReleaseNotes.txt @@ -0,0 +1,10 @@ +Version: 6.0.0 +Availability: NET Standard 2.0, NET Core 3.0 +  +# Upgrade Steps +- The Cuemon.AspNetCore.Mvc.Formatters.Xml namespace was removed with this version +- Any types found in the Cuemon.AspNetCore.Mvc.Formatters.Xml namespace was merged into the Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml namespace +  +# Improvements +- COMPATIBLE with the changes applied to NET Core 3 in regards to only allowing asynchronous I/O +  \ No newline at end of file From 48ef8ce584b0e15f347fb05401e9e771c8ad4a15 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 17:34:49 +0200 Subject: [PATCH 255/385] Updated package description, release notes and DocFx namespace description. --- ...c.Formatters.Newtonsoft.Json.Converters.md | 21 +++++++++++++++++- ...pNetCore.Mvc.Formatters.Newtonsoft.Json.md | 22 ++++++++++++++++++- ...spNetCore.Mvc.Formatters.Xml.Converters.md | 4 ++-- ...Core.Mvc.Formatters.Newtonsoft.Json.csproj | 4 ++-- .../Properties/PackageReleaseNotes.txt | 13 +++++++++++ 5 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md index 245683dbf..c22eb1b05 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters namespace contains extension methods that complements the Cuemon.Extensions.Newtonsoft.Json.Converters namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.Extensions.Newtonsoft.Json.Converters namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Newtonsoft.Json.Converters.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml)\ +[Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|JsonConverter|⬇️|`AddHttpExceptionDescriptorConverter`, `AddStringValuesConverter`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md index 52e848713..b959a0daa 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md @@ -2,4 +2,24 @@ uid: Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json namespace contains both types and extension methods that complements the Cuemon.Extensions.Newtonsoft.Json namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides JSON formatters for ASP.NET Core that is powered by Newtonsoft.Json. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.Extensions.Newtonsoft.Json namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Newtonsoft.Json.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json)\ +[Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IMvcBuilder|⬇️|`AddJsonSerializationFormatters`, `AddJsonFormatterOptions`| +|IMvcCoreBuilder|⬇️|`AddJsonSerializationFormatters`, `AddJsonFormatterOptions`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md index 0dd38103b..a3934ed00 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md @@ -2,11 +2,11 @@ uid: Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters summary: *content --- -The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters namespace contains extension methods that complements the Cuemon.Extensions.Xml.Converters namespace. +The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters namespace contains extension methods that complements the Cuemon.Extensions.Xml.Serialization.Converters namespace. Availability: NET Standard 2.0, NET Core 3.0 -Complements: [Cuemon.Extensions.Xml.Converters namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.html) 🔗 +Complements: [Cuemon.Extensions.Xml.Serialization.Converters namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Xml.Serialization.Converters.html) 🔗 Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml)\ diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj index 5dbe4fddb..a22fc909a 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj @@ -9,8 +9,8 @@ Cuemon Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json - The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json namespace contains extension methods and JSON formatters for ASP.NET Core MVC that uses the Newtonsoft.Json Nuget package. - extension-methods extensions json-converters + The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json namespace contains both types and extension methods that complements the Cuemon.Extensions.Newtonsoft.Json namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides JSON formatters for ASP.NET Core that is powered by Newtonsoft.Json. + extension-methods extensions json-converters add-json-serialization-formatters add-json-formatter-options diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..be4e71e5d --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Properties/PackageReleaseNotes.txt @@ -0,0 +1,13 @@ +Version: 6.0.0 +Availability: NET Standard 2.0, NET Core 3.0 +  +# Upgrade Steps +- The Cuemon.AspNetCore.Mvc.Formatters.Json namespace was removed with this version +- Any types found in the Cuemon.AspNetCore.Mvc.Formatters.Json namespace was merged into the Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json namespace +  +# Breaking Changes +- REMOVED DefaultJsonSerializerSettings class from the Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json namespace as the default settings is given by JsonFormatterOptions +  +# Improvements +- COMPATIBLE with the changes applied to NET Core 3 in regards to only allowing asynchronous I/O +  \ No newline at end of file From c3764ef6fef11d3390c9e6245a35ac694ef5413e Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 22:16:40 +0200 Subject: [PATCH 256/385] Renamed to be consistent. --- ...tExtensions.cs => CacheableAsyncResultFilterExtensions.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Cacheable/{CacheableAsyncResultFilterListExtensions.cs => CacheableAsyncResultFilterExtensions.cs} (95%) diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Cacheable/CacheableAsyncResultFilterListExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Cacheable/CacheableAsyncResultFilterExtensions.cs similarity index 95% rename from src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Cacheable/CacheableAsyncResultFilterListExtensions.cs rename to src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Cacheable/CacheableAsyncResultFilterExtensions.cs index cc7ef0862..8b2db73ba 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Cacheable/CacheableAsyncResultFilterListExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Cacheable/CacheableAsyncResultFilterExtensions.cs @@ -6,9 +6,9 @@ namespace Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable { /// - /// Extension methods for the interface. + /// Extension methods for the interface. /// - public static class CacheableAsyncResultFilterListExtensions + public static class CacheableAsyncResultFilterExtensions { /// /// Adds a HTTP related filter to the list. From 2d7a799e78c9f7062868735dd8c38c80f80d7a11 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 22:17:16 +0200 Subject: [PATCH 257/385] Minor refactoring of CacheBusing implementations. --- .../Configuration/CacheBustingOptions.cs | 2 +- .../Configuration/AssemblyCacheBusting.cs | 1 + .../Configuration/AssemblyCacheBustingOptions.cs | 1 + .../Configuration/CacheBusting.cs | 16 ---------------- .../Configuration/DynamicCacheBusting.cs | 1 + .../Configuration/DynamicCacheBustingOptions.cs | 5 +++-- .../Configuration/ServiceCollectionExtensions.cs | 14 ++++++++++++-- 7 files changed, 19 insertions(+), 21 deletions(-) rename src/{Cuemon.Extensions.AspNetCore.Mvc => Cuemon.AspNetCore}/Configuration/CacheBustingOptions.cs (95%) delete mode 100644 src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/CacheBusting.cs diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/CacheBustingOptions.cs b/src/Cuemon.AspNetCore/Configuration/CacheBustingOptions.cs similarity index 95% rename from src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/CacheBustingOptions.cs rename to src/Cuemon.AspNetCore/Configuration/CacheBustingOptions.cs index 01be125aa..a16ac6fe6 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/CacheBustingOptions.cs +++ b/src/Cuemon.AspNetCore/Configuration/CacheBustingOptions.cs @@ -1,4 +1,4 @@ -namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration +namespace Cuemon.AspNetCore.Configuration { /// /// Specifies options that is related to operations. diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBusting.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBusting.cs index 856cd9966..e95fd3c05 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBusting.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBusting.cs @@ -1,4 +1,5 @@ using System; +using Cuemon.AspNetCore.Configuration; using Cuemon.Extensions.Data.Integrity; using Cuemon.Security.Cryptography; using Microsoft.Extensions.Options; diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs index 60ac4d9ee..b0edc3031 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs @@ -1,4 +1,5 @@ using System.Reflection; +using Cuemon.AspNetCore.Configuration; using Cuemon.Security.Cryptography; namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/CacheBusting.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/CacheBusting.cs deleted file mode 100644 index f47b9bd6d..000000000 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/CacheBusting.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Cuemon.AspNetCore.Configuration; - -namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration -{ - /// - /// Represents a way to provide cache-busting capabilities. - /// - public abstract class CacheBusting : ICacheBusting - { - /// - /// Gets the version to be a part of the link you need cache-busting compatible. - /// - /// The version to be a part of the link you need cache-busting compatible. - public abstract string Version { get; } - } -} \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs index fed90ce4d..a81e10f0f 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs @@ -1,4 +1,5 @@ using System; +using Cuemon.AspNetCore.Configuration; using Cuemon.Configuration; using Microsoft.Extensions.Options; diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs index b68d5ab2a..6936e6d12 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs @@ -1,4 +1,5 @@ using System; +using Cuemon.AspNetCore.Configuration; namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration { @@ -28,13 +29,13 @@ public class DynamicCacheBustingOptions : CacheBustingOptions /// /// /// - /// 20 minutes + /// 12 hours /// /// /// public DynamicCacheBustingOptions() { - TimeToLive = TimeSpan.FromMinutes(20); + TimeToLive = TimeSpan.FromHours(12); PreferredLength = 8; PreferredCharacters = Alphanumeric.LettersAndNumbers; } diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs index c0e6c67f1..ae5f0176f 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs @@ -9,15 +9,25 @@ namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration public static class ServiceCollectionExtensions { /// - /// Adds a cache-busting service to the specified based on a default instance of . + /// Adds an service to the specified . /// /// The to add services to. /// An that can be used to further configure other services. - public static IServiceCollection AddCacheBusting(this IServiceCollection services) + public static IServiceCollection AddAssemblyCacheBusting(this IServiceCollection services) { return services.AddCacheBusting(); } + /// + /// Adds an service to the specified . + /// + /// The to add services to. + /// An that can be used to further configure other services. + public static IServiceCollection AddDynamicCacheBusting(this IServiceCollection services) + { + return services.AddCacheBusting(); + } + /// /// Adds a cache-busting service to the specified . /// From 0549ef8ea2ad5d8b266ba7d521bb224a9402d4c3 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 22:17:39 +0200 Subject: [PATCH 258/385] Changed to inherit from IAsyncResultFilter. --- .../Cacheable/ICacheableAsyncResultFilter.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/ICacheableAsyncResultFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/ICacheableAsyncResultFilter.cs index 7421e615f..797d5b534 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/ICacheableAsyncResultFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/ICacheableAsyncResultFilter.cs @@ -1,19 +1,11 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.Filters; namespace Cuemon.AspNetCore.Mvc.Filters.Cacheable { /// - /// A filter that asynchronously surrounds execution of action results successfully returned from an action. + /// A filter tailored to the cacheable flows, that asynchronously surrounds execution of action results successfully returned from an action. /// - public interface ICacheableAsyncResultFilter + public interface ICacheableAsyncResultFilter : IAsyncResultFilter { - /// - /// Called asynchronously before the action result. - /// - /// The . - /// The . Invoked to execute the next result filter or the result itself. - /// A that on completion indicates the filter has executed. - Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next); } } \ No newline at end of file From 59b2d8b8c5230dee09f47cbf65e143944f11ab3f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 3 Oct 2020 22:43:25 +0200 Subject: [PATCH 259/385] Updated package description, release notes and DocFx namespace description. --- ...Extensions.AspNetCore.Mvc.Configuration.md | 21 ++++++++++++++++- ...nsions.AspNetCore.Mvc.Filters.Cacheable.md | 21 ++++++++++++++++- ...ions.AspNetCore.Mvc.Filters.Diagnostics.md | 21 ++++++++++++++++- ...mon.Extensions.AspNetCore.Mvc.Rendering.md | 21 ++++++++++++++++- .../Cuemon.Extensions.AspNetCore.Mvc.md | 23 ++++++++++++++++++- .../Cuemon.Extensions.AspNetCore.Mvc.csproj | 4 ++-- .../Properties/PackageReleaseNotes.txt | 12 ++++++++++ 7 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md index fbf645b09..ac293f0bd 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.AspNetCore.Mvc.Configuration summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Mvc.Configuration namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Configuration namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides a set of different cache busting strategies for ASP.NET Core that can be easily customized. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.AspNetCore.Configuration namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Configuration.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore.Mvc (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore.Mvc)\ +[Cuemon.Extensions.AspNetCore.Mvc (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore.Mvc) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IServiceCollection|⬇️|`AddCacheBusting{T}`, `AddAssemblyCacheBusting`, `AddDynamicCacheBusting`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md index 9176a52f9..99bc2fd00 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable namespace contains extension methods that complements the Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Mvc.Filters.Cacheable.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore.Mvc (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore.Mvc)\ +[Cuemon.Extensions.AspNetCore.Mvc (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore.Mvc) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|ICacheableAsyncResultFilter|⬇️|`AddFilter{T, TOptions}`, `InsertFilter{T, TOptions}`, `AddEntityTagHeader`, `AddLastModifiedHeader`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md index 3f93f281d..c7fff0eb3 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics namespace contains extension methods that complements the Cuemon.AspNetCore.Mvc.Filters.Diagnostics namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.AspNetCore.Mvc.Filters.Diagnostics namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore.Mvc (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore.Mvc)\ +[Cuemon.Extensions.AspNetCore.Mvc (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore.Mvc) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|FaultResolver|⬇️|`Add{T}`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md index 7dbfc3412..d0ecc5ea5 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.AspNetCore.Mvc.Rendering summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Mvc.Rendering namespace contains extension methods that complements the Microsoft.AspNetCore.Mvc.Rendering namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Mvc.Rendering namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.rendering?view=aspnetcore-3.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore.Mvc (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore.Mvc)\ +[Cuemon.Extensions.AspNetCore.Mvc (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore.Mvc) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IHtmlHelper|⬇️|`UseWhen`, `UseWhen{T}`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md index af2cb92fd..515c15635 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md @@ -2,4 +2,25 @@ uid: Cuemon.Extensions.AspNetCore.Mvc summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Mvc namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Mvc namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides a set of different cache busting strategies for ASP.NET Core that can be easily customized. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.AspNetCore.Mvc namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Mvc.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore.Mvc (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore.Mvc)\ +[Cuemon.Extensions.AspNetCore.Mvc (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore.Mvc) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IApplicationBuilder|⬇️|`UseWhen`| +|Object|⬇️|`MakeCacheable`, `MakeCacheable{T}`| +|ViewDataDictionary|⬇️|`AddBreadcrumbs{T}`, `GetBreadcrumbs`| \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj index 62cb73a30..f05ada8fd 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj @@ -8,8 +8,8 @@ Cuemon.Extensions.AspNetCore.Mvc Cuemon.Extensions.AspNetCore.Mvc - The Cuemon.Extensions.AspNetCore.Mvc namespace contains extension methods and features related to the Cuemon.AspNetCore.Mvc namespace. - extension-methods extensions assembly-cache-busting cache-busting dynamic-cache-busting use-when make-cacheable + The Cuemon.Extensions.AspNetCore.Mvc namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Mvc namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides a set of different cache busting strategies for ASP.NET Core that can be easily customized. + extension-methods extensions add-assembly-cache-busting add-cache-busting add-dynamic-cache-busting use-when make-cacheable diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..24d260885 --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt @@ -0,0 +1,12 @@ +Version: 6.0.0 +Availability: NET Standard 2.0, NET Core 3.0 +  +# Breaking Changes +- RENAMED ToCacheableObjectResult{T} --> MakeCacheable{T} on the CacheableObjectResultExtensions class in the Cuemon.Extensions.AspNetCore.Mvc (also included a non-generic variant: MakeCacheable) +  +# New Features +- ADDED ServiceCollectionExtensions class in the Cuemon.Extensions.AspNetCore.Mvc.Configuration namespace that consist of extension methods for the IServiceCollection interface: AddAssemblyCacheBusting, AddDynamicCacheBusting, AddCacheBusting{T} +  +# Improvements +- COMPATIBLE with the changes applied to NET Core 3 in regards to only allowing asynchronous I/O +  \ No newline at end of file From 9d1dcf56778be61187a32734f5b14e9c3e816aa6 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 4 Oct 2020 02:48:59 +0200 Subject: [PATCH 260/385] Added one overload for consistency. --- .../Throttling/ServiceCollectionExtensions.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Cuemon.Extensions.AspNetCore/Http/Throttling/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Http/Throttling/ServiceCollectionExtensions.cs index 52b8da531..06decdfeb 100644 --- a/src/Cuemon.Extensions.AspNetCore/Http/Throttling/ServiceCollectionExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Http/Throttling/ServiceCollectionExtensions.cs @@ -9,13 +9,24 @@ namespace Cuemon.Extensions.AspNetCore.Http.Throttling public static class ServiceCollectionExtensions { /// - /// Adds a memory-based throttling cache service to the specified . + /// Adds a service to the specified . /// /// The to add services to. /// An that can be used to further configure other services. - public static IServiceCollection AddMemoryThrottling(this IServiceCollection services) + public static IServiceCollection AddMemoryThrottlingCache(this IServiceCollection services) { - services.AddSingleton(); + return services.AddThrottlingCache(); + } + + /// + /// Adds a throttling cache service to the specified . + /// + /// The to add services to. + /// An that can be used to further configure other services. + public static IServiceCollection AddThrottlingCache(this IServiceCollection services) where T : class, IThrottlingCache + { + Validator.ThrowIfNull(services, nameof(services)); + services.AddSingleton(); return services; } } From b71f2401e804e5641c26a951fdeecdd4c37ea79e Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 4 Oct 2020 14:49:15 +0200 Subject: [PATCH 261/385] Changed TryAddOrUpdate* to AddOrUpdate for consistency and to avoid confusion. Boolean return does not make sense for covering both Add and Update. Consequence changes applied. --- .../Filters/Cacheable/HttpEntityTagHeaderOptions.cs | 4 ++-- .../Filters/Cacheable/HttpLastModifiedHeaderOptions.cs | 2 +- .../Filters/Diagnostics/TimeMeasuringFilter.cs | 4 ++-- .../Filters/Headers/UserAgentSentinelFilter.cs | 2 +- .../Filters/Throttling/ThrottlingSentinelFilter.cs | 2 +- .../Http/Headers/HeaderDictionaryDecoratorExtensions.cs | 9 ++++----- .../Extensions/Http/HttpResponseDecoratorExtensions.cs | 8 ++++---- .../Http/Throttling/ThrottlingSentinelMiddleware.cs | 2 +- .../Infrastructure/AspNetCoreInfrastructure.cs | 6 +++--- .../Collections/Generic/DictionaryDecoratorExtensions.cs | 4 ++-- .../ViewDataDictionaryExtensions.cs | 2 +- .../Http/HeaderDictionaryExtensions.cs | 6 +++--- .../Http/HttpResponseExtensions.cs | 8 ++++---- .../DictionaryExtensions.cs | 4 ++-- .../Serialization/Converters/DefaultXmlConverter.cs | 2 +- .../Generic/DictionaryDecoratorExtensionsTest.cs | 9 +++------ 16 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs index 647f73790..71bd0edf4 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpEntityTagHeaderOptions.cs @@ -60,14 +60,14 @@ public HttpEntityTagHeaderOptions() EntityTagProvider = (integrity, context) => { var builder = new ChecksumBuilder(integrity.Checksum.GetBytes(), () => HashFactory.CreateFnv128()); - Decorator.Enclose(context.Response).TryAddOrUpdateEntityTagHeader(context.Request, builder, integrity.Validation == EntityDataIntegrityValidation.Weak); + Decorator.Enclose(context.Response).AddOrUpdateEntityTagHeader(context.Request, builder, integrity.Validation == EntityDataIntegrityValidation.Weak); }; EntityTagResponseParser = (body, request, response) => { var ms = new MemoryStream(); Decorator.Enclose(body).CopyStream(ms); var builder = new ChecksumBuilder(ms.ToArray(), () => UnkeyedHashFactory.CreateCryptoMd5()); - Decorator.Enclose(response).TryAddOrUpdateEntityTagHeader(request, builder); + Decorator.Enclose(response).AddOrUpdateEntityTagHeader(request, builder); }; UseEntityTagResponseParser = false; } diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs index f33249bd7..eb30642b1 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Cacheable/HttpLastModifiedHeaderOptions.cs @@ -38,7 +38,7 @@ public HttpLastModifiedHeaderOptions() { LastModifiedProvider = (timestamp, context) => { - Decorator.Enclose(context.Response).TryAddOrUpdateLastModifiedHeader(context.Request, timestamp.Modified ?? timestamp.Created); + Decorator.Enclose(context.Response).AddOrUpdateLastModifiedHeader(context.Request, timestamp.Modified ?? timestamp.Created); }; } diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasuringFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasuringFilter.cs index c6386d512..2ce81e3b3 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasuringFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasuringFilter.cs @@ -81,8 +81,8 @@ public override void OnActionExecuted(ActionExecutedContext context) TimeMeasure.CompletedCallback?.Invoke(Profiler); if (!Options.SuppressHeaderPredicate(Environment)) { - if (Options.UseServerTimingHeader) { Decorator.Enclose(context.HttpContext.Response.Headers).TryAddOrUpdateHeader("Server-Timing", FormattableString.Invariant($"CPU;dur={Profiler.Elapsed.TotalMilliseconds.ToString("N1", CultureInfo.InvariantCulture)}")); } - if (Options.UseCustomHeader) { Decorator.Enclose(context.HttpContext.Response.Headers).TryAddOrUpdateHeader(Options.HeaderName, Profiler.ToString()); } + if (Options.UseServerTimingHeader) { Decorator.Enclose(context.HttpContext.Response.Headers).AddOrUpdateHeader("Server-Timing", FormattableString.Invariant($"CPU;dur={Profiler.Elapsed.TotalMilliseconds.ToString("N1", CultureInfo.InvariantCulture)}")); } + if (Options.UseCustomHeader) { Decorator.Enclose(context.HttpContext.Response.Headers).AddOrUpdateHeader(Options.HeaderName, Profiler.ToString()); } } } } diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs index 61744fa8b..d416da957 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs @@ -32,7 +32,7 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context await AspNetCoreInfrastructure.InvokeUserAgentSentinelAsync(context.HttpContext, Options, (message, response) => { response.StatusCode = (int) message.StatusCode; - Decorator.Enclose(response.Headers).TryAddOrUpdateHeaders(message.Headers); + Decorator.Enclose(response.Headers).AddOrUpdateHeaders(message.Headers); }).ConfigureAwait(false); await next().ConfigureAwait(false); } diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelFilter.cs index 64ca787be..ba64209c6 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelFilter.cs @@ -37,7 +37,7 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context await AspNetCoreInfrastructure.InvokeThrottlerSentinelAsync(context.HttpContext, ThrottlingCache, Options, (message, response) => { response.StatusCode = (int) message.StatusCode; - Decorator.Enclose(response.Headers).TryAddOrUpdateHeaders(message.Headers); + Decorator.Enclose(response.Headers).AddOrUpdateHeaders(message.Headers); }).ConfigureAwait(false); await next().ConfigureAwait(false); } diff --git a/src/Cuemon.AspNetCore/Extensions/Http/Headers/HeaderDictionaryDecoratorExtensions.cs b/src/Cuemon.AspNetCore/Extensions/Http/Headers/HeaderDictionaryDecoratorExtensions.cs index cd51ed490..882722cbf 100644 --- a/src/Cuemon.AspNetCore/Extensions/Http/Headers/HeaderDictionaryDecoratorExtensions.cs +++ b/src/Cuemon.AspNetCore/Extensions/Http/Headers/HeaderDictionaryDecoratorExtensions.cs @@ -24,14 +24,13 @@ public static class HeaderDictionaryDecoratorExtensions /// /// cannot be null. /// - public static bool TryAddOrUpdateHeader(this IDecorator decorator, string key, StringValues value, bool useAsciiEncodingConversion = true) + public static void AddOrUpdateHeader(this IDecorator decorator, string key, StringValues value, bool useAsciiEncodingConversion = true) { var headerValue = useAsciiEncodingConversion ? new StringValues(Decorator.Enclose(value).ToAsciiEncodedString()) : value; if (headerValue != StringValues.Empty) { - return decorator.TryAddOrUpdate(key, Decorator.Enclose(headerValue.ToString().Where(c => !char.IsControl(c))).ToStringEquivalent()); + decorator.AddOrUpdate(key, Decorator.Enclose(headerValue.ToString().Where(c => !char.IsControl(c))).ToStringEquivalent()); } - return false; } /// @@ -39,12 +38,12 @@ public static bool TryAddOrUpdateHeader(this IDecorator decor /// /// The to extend. /// The to copy. - public static void TryAddOrUpdateHeaders(this IDecorator decorator, HttpResponseHeaders responseHeaders) + public static void AddOrUpdateHeaders(this IDecorator decorator, HttpResponseHeaders responseHeaders) { if (decorator == null || responseHeaders == null) { return; } foreach (var header in responseHeaders) { - decorator.TryAddOrUpdate(header.Key, header.Value != null ? DelimitedString.Create(header.Value) : ""); + decorator.AddOrUpdate(header.Key, header.Value != null ? DelimitedString.Create(header.Value) : ""); } } } diff --git a/src/Cuemon.AspNetCore/Extensions/Http/HttpResponseDecoratorExtensions.cs b/src/Cuemon.AspNetCore/Extensions/Http/HttpResponseDecoratorExtensions.cs index 23e270e54..a59ce7d23 100644 --- a/src/Cuemon.AspNetCore/Extensions/Http/HttpResponseDecoratorExtensions.cs +++ b/src/Cuemon.AspNetCore/Extensions/Http/HttpResponseDecoratorExtensions.cs @@ -28,14 +28,14 @@ public static class HttpResponseDecoratorExtensions /// cannot be null -or- /// cannot be null. /// - public static void TryAddOrUpdateEntityTagHeader(this IDecorator decorator, HttpRequest request, ChecksumBuilder builder, bool isWeak = false) + public static void AddOrUpdateEntityTagHeader(this IDecorator decorator, HttpRequest request, ChecksumBuilder builder, bool isWeak = false) { Validator.ThrowIfNull(decorator, nameof(decorator)); Validator.ThrowIfNull(request, nameof(request)); Validator.ThrowIfNull(builder, nameof(builder)); builder = Decorator.Enclose(builder).CombineWith(request.Headers[HeaderNames.Accept]); if (Decorator.Enclose(decorator.Inner.StatusCode).IsSuccessStatusCode() && Decorator.Enclose(request).IsClientSideResourceCached(builder)) { decorator.Inner.StatusCode = StatusCodes.Status304NotModified; } - Decorator.Enclose(decorator.Inner.Headers).TryAddOrUpdate(HeaderNames.ETag, new StringValues(Decorator.Enclose(builder).ToEntityTagHeaderValue(isWeak).ToString())); + Decorator.Enclose(decorator.Inner.Headers).AddOrUpdate(HeaderNames.ETag, new StringValues(Decorator.Enclose(builder).ToEntityTagHeaderValue(isWeak).ToString())); } /// @@ -48,12 +48,12 @@ public static void TryAddOrUpdateEntityTagHeader(this IDecorator d /// cannot be null -or- /// cannot be null. /// - public static void TryAddOrUpdateLastModifiedHeader(this IDecorator decorator, HttpRequest request, DateTime lastModified) + public static void AddOrUpdateLastModifiedHeader(this IDecorator decorator, HttpRequest request, DateTime lastModified) { Validator.ThrowIfNull(decorator, nameof(decorator)); Validator.ThrowIfNull(request, nameof(request)); if (Decorator.Enclose(decorator.Inner.StatusCode).IsSuccessStatusCode() && Decorator.Enclose(request).IsClientSideResourceCached(lastModified)) { decorator.Inner.StatusCode = StatusCodes.Status304NotModified; } - Decorator.Enclose(decorator.Inner.Headers).TryAddOrUpdate(HeaderNames.LastModified, new StringValues(lastModified.ToUniversalTime().ToString("R", DateTimeFormatInfo.InvariantInfo))); + Decorator.Enclose(decorator.Inner.Headers).AddOrUpdate(HeaderNames.LastModified, new StringValues(lastModified.ToUniversalTime().ToString("R", DateTimeFormatInfo.InvariantInfo))); } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs b/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs index f63526dbb..6032178fd 100644 --- a/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs +++ b/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs @@ -45,7 +45,7 @@ public override async Task InvokeAsync(HttpContext context, IThrottlingCache di) await AspNetCoreInfrastructure.InvokeThrottlerSentinelAsync(context, di, Options, async (message, response) => { response.StatusCode = (int)message.StatusCode; - Decorator.Enclose(response.Headers).TryAddOrUpdateHeaders(message.Headers); + Decorator.Enclose(response.Headers).AddOrUpdateHeaders(message.Headers); await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); }).ConfigureAwait(false); } diff --git a/src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs b/src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs index b309bb34a..844dfe696 100644 --- a/src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs +++ b/src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs @@ -58,9 +58,9 @@ public static async Task InvokeThrottlerSentinelAsync(HttpContext context, IThro var window = new TimeRange(utcNow, tr.Expires); var delta = window.Duration; var reset = utcNow.Add(delta); - Decorator.Enclose(context.Response.Headers).TryAddOrUpdate(options.RateLimitHeaderName, tr.Quota.RateLimit.ToString(CultureInfo.InvariantCulture)); - Decorator.Enclose(context.Response.Headers).TryAddOrUpdate(options.RateLimitRemainingHeaderName, Math.Max(tr.Quota.RateLimit - tr.Total, 0).ToString(CultureInfo.InvariantCulture)); - Decorator.Enclose(context.Response.Headers).TryAddOrUpdate(options.RateLimitResetHeaderName, Decorator.Enclose(reset).ToUnixEpochTime().ToString(CultureInfo.InvariantCulture)); + Decorator.Enclose(context.Response.Headers).AddOrUpdate(options.RateLimitHeaderName, tr.Quota.RateLimit.ToString(CultureInfo.InvariantCulture)); + Decorator.Enclose(context.Response.Headers).AddOrUpdate(options.RateLimitRemainingHeaderName, Math.Max(tr.Quota.RateLimit - tr.Total, 0).ToString(CultureInfo.InvariantCulture)); + Decorator.Enclose(context.Response.Headers).AddOrUpdate(options.RateLimitResetHeaderName, Decorator.Enclose(reset).ToUnixEpochTime().ToString(CultureInfo.InvariantCulture)); if (tr.Total > tr.Quota.RateLimit && tr.Expires > utcNow) { var message = options.ResponseBroker?.Invoke(delta, reset); diff --git a/src/Cuemon.Core/Extensions/Collections/Generic/DictionaryDecoratorExtensions.cs b/src/Cuemon.Core/Extensions/Collections/Generic/DictionaryDecoratorExtensions.cs index 3c29af769..6ed5b2eb6 100644 --- a/src/Cuemon.Core/Extensions/Collections/Generic/DictionaryDecoratorExtensions.cs +++ b/src/Cuemon.Core/Extensions/Collections/Generic/DictionaryDecoratorExtensions.cs @@ -135,11 +135,11 @@ public static bool TryAdd(this IDecorator cannot be null -or- /// cannot be null. /// - public static bool TryAddOrUpdate(this IDecorator> decorator, TKey key, TValue value) + public static void AddOrUpdate(this IDecorator> decorator, TKey key, TValue value) { Validator.ThrowIfNull(decorator, nameof(decorator)); Validator.ThrowIfNull(key, nameof(key)); - return decorator.Inner.ContainsKey(key) ? Patterns.TryInvoke(() => { decorator.Inner[key] = value; }) : TryAdd(decorator, key, value); + Condition.FlipFlop(decorator.Inner.ContainsKey(key), () => Patterns.TryInvoke(() => { decorator.Inner[key] = value; }), () => TryAdd(decorator, key, value)); } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/ViewDataDictionaryExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/ViewDataDictionaryExtensions.cs index f453ca8b1..36407f579 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/ViewDataDictionaryExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/ViewDataDictionaryExtensions.cs @@ -42,7 +42,7 @@ public static void AddBreadcrumbs(this ViewDataDictionary viewData, Controlle list.Add(bc); } - Decorator.Enclose(viewData).TryAddOrUpdate(BreadcrumbKey, list); + Decorator.Enclose(viewData).AddOrUpdate(BreadcrumbKey, list); } /// diff --git a/src/Cuemon.Extensions.AspNetCore/Http/HeaderDictionaryExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Http/HeaderDictionaryExtensions.cs index 2e180577d..8574127cc 100644 --- a/src/Cuemon.Extensions.AspNetCore/Http/HeaderDictionaryExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Http/HeaderDictionaryExtensions.cs @@ -17,10 +17,10 @@ public static class HeaderDictionaryExtensions /// The string to use as the key of the element to add. /// The string to use as the value of the element to add. /// if set to true an ASCII encoding conversion is applied to the . - public static bool TryAddOrUpdateHeader(this IHeaderDictionary dictionary, string key, StringValues value, bool useAsciiEncodingConversion = true) + public static void AddOrUpdateHeader(this IHeaderDictionary dictionary, string key, StringValues value, bool useAsciiEncodingConversion = true) { Validator.ThrowIfNull(dictionary, nameof(dictionary)); - return Decorator.Enclose(dictionary).TryAddOrUpdateHeader(key, value, useAsciiEncodingConversion); + Decorator.Enclose(dictionary).AddOrUpdateHeader(key, value, useAsciiEncodingConversion); } /// @@ -30,7 +30,7 @@ public static bool TryAddOrUpdateHeader(this IHeaderDictionary dictionary, strin /// The to copy. public static void AddOrUpdateHeaders(this IHeaderDictionary dictionary, HttpResponseHeaders responseHeaders) { - Decorator.Enclose(dictionary).TryAddOrUpdateHeaders(responseHeaders); + Decorator.Enclose(dictionary).AddOrUpdateHeaders(responseHeaders); } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore/Http/HttpResponseExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Http/HttpResponseExtensions.cs index aa9fae794..b5944ac03 100644 --- a/src/Cuemon.Extensions.AspNetCore/Http/HttpResponseExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Http/HttpResponseExtensions.cs @@ -24,10 +24,10 @@ public static class HttpResponseExtensions /// cannot be null -or- /// cannot be null. /// - public static void TryAddOrUpdateEntityTagHeader(this HttpResponse response, HttpRequest request, ChecksumBuilder builder, bool isWeak = false) + public static void AddOrUpdateEntityTagHeader(this HttpResponse response, HttpRequest request, ChecksumBuilder builder, bool isWeak = false) { Validator.ThrowIfNull(response, nameof(response)); - Decorator.Enclose(response).TryAddOrUpdateEntityTagHeader(request, builder, isWeak); + Decorator.Enclose(response).AddOrUpdateEntityTagHeader(request, builder, isWeak); } /// @@ -40,10 +40,10 @@ public static void TryAddOrUpdateEntityTagHeader(this HttpResponse response, Htt /// cannot be null -or- /// cannot be null. /// - public static void TryAddOrUpdateLastModifiedHeader(this HttpResponse response, HttpRequest request, DateTime lastModified) + public static void AddOrUpdateLastModifiedHeader(this HttpResponse response, HttpRequest request, DateTime lastModified) { Validator.ThrowIfNull(response, nameof(response)); - Decorator.Enclose(response).TryAddOrUpdateLastModifiedHeader(request, lastModified); + Decorator.Enclose(response).AddOrUpdateLastModifiedHeader(request, lastModified); } /// diff --git a/src/Cuemon.Extensions.Collections.Generic/DictionaryExtensions.cs b/src/Cuemon.Extensions.Collections.Generic/DictionaryExtensions.cs index 2cbb337ef..6e415d416 100644 --- a/src/Cuemon.Extensions.Collections.Generic/DictionaryExtensions.cs +++ b/src/Cuemon.Extensions.Collections.Generic/DictionaryExtensions.cs @@ -128,10 +128,10 @@ public static bool TryAdd(this IDictionary dictionar /// cannot be null -or- /// cannot be null. /// - public static bool TryAddOrUpdate(this IDictionary dictionary, TKey key, TValue value) + public static void AddOrUpdate(this IDictionary dictionary, TKey key, TValue value) { Validator.ThrowIfNull(dictionary, nameof(dictionary)); - return Decorator.Enclose(dictionary).TryAddOrUpdate(key, value); + Decorator.Enclose(dictionary).AddOrUpdate(key, value); } } } \ No newline at end of file diff --git a/src/Cuemon.Xml/Serialization/Converters/DefaultXmlConverter.cs b/src/Cuemon.Xml/Serialization/Converters/DefaultXmlConverter.cs index 9b3a19e33..1f92d15b5 100644 --- a/src/Cuemon.Xml/Serialization/Converters/DefaultXmlConverter.cs +++ b/src/Cuemon.Xml/Serialization/Converters/DefaultXmlConverter.cs @@ -184,7 +184,7 @@ private object ParseReadXmlDefault(XmlReader reader, Type valueType) break; case XmlNodeType.CDATA: case XmlNodeType.Text: - Decorator.Enclose(values).TryAddOrUpdate(key, reader.Value); + Decorator.Enclose(values).AddOrUpdate(key, reader.Value); break; } } diff --git a/test/Cuemon.Core.Tests/Collections/Generic/DictionaryDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/Collections/Generic/DictionaryDecoratorExtensionsTest.cs index 5fd19873b..fb95c59fd 100644 --- a/test/Cuemon.Core.Tests/Collections/Generic/DictionaryDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/Collections/Generic/DictionaryDecoratorExtensionsTest.cs @@ -88,11 +88,9 @@ public void Extend_Dictionary_With_TryAddOrUpdate_Add_And_Update_Expect_True() var txt = "First Legion"; var subtxt = " Rules the Galaxy"; Assert.Equal(500, dic.Count); - var added = Decorator.Enclose(dic).TryAddOrUpdate(key, txt); - Assert.True(added); + Decorator.Enclose(dic).AddOrUpdate(key, txt); Assert.Equal(txt, dic[key]); - var updated = Decorator.Enclose(dic).TryAddOrUpdate(key, txt + subtxt); - Assert.True(updated); + Decorator.Enclose(dic).AddOrUpdate(key, txt + subtxt); Assert.Equal(string.Concat(txt, subtxt), dic[key]); } @@ -104,8 +102,7 @@ public void Extend_Dictionary_With_TryAddOrUpdate_Update_Expect_True() var txt = "First Legion"; Assert.Equal(500, dic.Count); Assert.Equal(elm.Value, dic.Last().Value); - var updated = Decorator.Enclose(dic).TryAddOrUpdate(elm.Key, txt); - Assert.True(updated); + Decorator.Enclose(dic).AddOrUpdate(elm.Key, txt); Assert.Equal(txt, dic.Last().Value); Assert.Equal(500, dic.Count); } From cf3bf129e9904e60d4ffaa56f2c0e3e28809431e Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 4 Oct 2020 14:49:55 +0200 Subject: [PATCH 262/385] Changed TryAddOrUpdate* to AddOrUpdate for consistency and to avoid confusion. Boolean return does not make sense for covering both Add and Update. Consequence changes applied. --- .../Http/Headers/CorrelationIdentifierMiddleware.cs | 2 +- .../Http/Headers/RequestIdentifierMiddleware.cs | 2 +- .../Http/Headers/UserAgentSentinelMiddleware.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cuemon.AspNetCore/Http/Headers/CorrelationIdentifierMiddleware.cs b/src/Cuemon.AspNetCore/Http/Headers/CorrelationIdentifierMiddleware.cs index 3262106c3..3d14e71ba 100644 --- a/src/Cuemon.AspNetCore/Http/Headers/CorrelationIdentifierMiddleware.cs +++ b/src/Cuemon.AspNetCore/Http/Headers/CorrelationIdentifierMiddleware.cs @@ -45,7 +45,7 @@ public override Task InvokeAsync(HttpContext context) Decorator.Enclose(context.Items).TryAdd(HttpContextItemsKey, correlationId); context.Response.OnStarting(() => { - Decorator.Enclose(context.Response.Headers).TryAddOrUpdate(Options.HeaderName, correlationId); + Decorator.Enclose(context.Response.Headers).AddOrUpdate(Options.HeaderName, correlationId); return Task.CompletedTask; }); return Next(context); diff --git a/src/Cuemon.AspNetCore/Http/Headers/RequestIdentifierMiddleware.cs b/src/Cuemon.AspNetCore/Http/Headers/RequestIdentifierMiddleware.cs index 439d42517..ced05a16e 100644 --- a/src/Cuemon.AspNetCore/Http/Headers/RequestIdentifierMiddleware.cs +++ b/src/Cuemon.AspNetCore/Http/Headers/RequestIdentifierMiddleware.cs @@ -45,7 +45,7 @@ public override Task InvokeAsync(HttpContext context) Decorator.Enclose(context.Items).TryAdd(HttpContextItemsKey, requestId); context.Response.OnStarting(() => { - Decorator.Enclose(context.Response.Headers).TryAddOrUpdate(Options.HeaderName, requestId); + Decorator.Enclose(context.Response.Headers).AddOrUpdate(Options.HeaderName, requestId); return Task.CompletedTask; }); return Next(context); diff --git a/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs b/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs index f2af71337..9722f1765 100644 --- a/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs +++ b/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs @@ -43,7 +43,7 @@ public override async Task InvokeAsync(HttpContext context) await AspNetCoreInfrastructure.InvokeUserAgentSentinelAsync(context, Options, async (message, response) => { response.StatusCode = (int) message.StatusCode; - Decorator.Enclose(response.Headers).TryAddOrUpdateHeaders(message.Headers); + Decorator.Enclose(response.Headers).AddOrUpdateHeaders(message.Headers); await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); }).ConfigureAwait(false); } From dd7699064699917ea285a709dd678c851c41c126 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 4 Oct 2020 14:50:18 +0200 Subject: [PATCH 263/385] Removed Implements as part of summary. --- src/Cuemon.AspNetCore/Http/Throttling/IThrottlingCache.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Cuemon.AspNetCore/Http/Throttling/IThrottlingCache.cs b/src/Cuemon.AspNetCore/Http/Throttling/IThrottlingCache.cs index 923bca83b..e7403279f 100644 --- a/src/Cuemon.AspNetCore/Http/Throttling/IThrottlingCache.cs +++ b/src/Cuemon.AspNetCore/Http/Throttling/IThrottlingCache.cs @@ -4,7 +4,6 @@ namespace Cuemon.AspNetCore.Http.Throttling { /// /// Specifies the contract for the storage of a throttling cache. - /// Implements the . /// /// public interface IThrottlingCache : IDictionary From 29781e59f2138829b350288006eb15d5b4488118 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 4 Oct 2020 14:50:54 +0200 Subject: [PATCH 264/385] Renamed methods for consistency. --- .../Builder/ApplicationBuilderExtensions.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs index a2197e4bf..474e3bce4 100644 --- a/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs @@ -18,7 +18,8 @@ public static class ApplicationBuilderExtensions /// The type that provides the mechanisms to configure an application’s request pipeline. /// The middleware which need to be configured. /// A reference to this instance after the operation has completed. - public static IApplicationBuilder UseHostingEnvironmentHeader(this IApplicationBuilder builder, Action setup = null) + /// Default HTTP header name is X-Hosting-Environment. + public static IApplicationBuilder UseHostingEnvironment(this IApplicationBuilder builder, Action setup = null) { return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); } @@ -30,7 +31,7 @@ public static IApplicationBuilder UseHostingEnvironmentHeader(this IApplicationB /// The middleware which need to be configured. /// A reference to this instance after the operation has completed. /// Default HTTP header name is X-Correlation-ID. - public static IApplicationBuilder UseCorrelationIdentifierHeader(this IApplicationBuilder builder, Action setup = null) + public static IApplicationBuilder UseCorrelationIdentifier(this IApplicationBuilder builder, Action setup = null) { return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); } @@ -42,7 +43,7 @@ public static IApplicationBuilder UseCorrelationIdentifierHeader(this IApplicati /// The middleware which need to be configured. /// A reference to this instance after the operation has completed. /// Default HTTP header name is X-Request-ID. - public static IApplicationBuilder UseRequestIdentifierHeader(this IApplicationBuilder builder, Action setup = null) + public static IApplicationBuilder UseRequestIdentifier(this IApplicationBuilder builder, Action setup = null) { return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); } @@ -59,12 +60,12 @@ public static IApplicationBuilder UseUserAgentSentinel(this IApplicationBuilder } /// - /// Adds a custom rate limiting / throttling to the request execution pipeline. + /// Adds a HTTP requests rate limiting / throttling guard to the request execution pipeline. /// /// The type that provides the mechanisms to configure an application’s request pipeline. /// The middleware which need to be configured. /// A reference to this instance after the operation has completed. - public static IApplicationBuilder UseCustomThrottlingSentinel(this IApplicationBuilder builder, Action setup) + public static IApplicationBuilder UseThrottlingSentinel(this IApplicationBuilder builder, Action setup) { return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); } From 74cf4b7f0b4d89c5068c2ba26b0bd0baf28401f1 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 4 Oct 2020 14:51:27 +0200 Subject: [PATCH 265/385] Changed constraint from abstract class to class, interface. --- .../Configuration/ServiceCollectionExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs index ae5f0176f..fc90c170d 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs @@ -19,7 +19,7 @@ public static IServiceCollection AddAssemblyCacheBusting(this IServiceCollection } /// - /// Adds an service to the specified . + /// Adds a service to the specified . /// /// The to add services to. /// An that can be used to further configure other services. @@ -33,7 +33,7 @@ public static IServiceCollection AddDynamicCacheBusting(this IServiceCollection /// /// The to add services to. /// An that can be used to further configure other services. - public static IServiceCollection AddCacheBusting(this IServiceCollection services) where T : CacheBusting + public static IServiceCollection AddCacheBusting(this IServiceCollection services) where T : class, ICacheBusting { Validator.ThrowIfNull(services, nameof(services)); services.AddSingleton(); From 86fe86be2d3355d054465b5ce95b15f8e94aeab9 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 4 Oct 2020 14:53:37 +0200 Subject: [PATCH 266/385] Updated package description, release notes and DocFx namespace description. --- .../Cuemon.Extensions.AspNetCore.Builder.md | 21 +++++++++++++++- ...on.Extensions.AspNetCore.Data.Integrity.md | 22 ++++++++++++++++- ...n.Extensions.AspNetCore.Http.Throttling.md | 21 +++++++++++++++- .../Cuemon.Extensions.AspNetCore.Http.md | 24 ++++++++++++++++++- ...Extensions.AspNetCore.Mvc.Configuration.md | 2 +- .../Cuemon.Extensions.AspNetCore.Mvc.md | 2 +- .../Cuemon.Extensions.AspNetCore.md | 15 +++++++++++- .../Cuemon.Extensions.AspNetCore.Mvc.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 5 +--- .../Cuemon.Extensions.AspNetCore.csproj | 2 +- .../Properties/PackageReleaseNotes.txt | 23 ++++++++++++++++++ 11 files changed, 126 insertions(+), 13 deletions(-) create mode 100644 src/Cuemon.Extensions.AspNetCore/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md index 074c209f5..efd68b556 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.AspNetCore.Builder summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Builder namespace contains extension methods that complements the Cuemon.AspNetCore.Builder namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.AspNetCore.Builder namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Builder.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore/Builder)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore/Builder)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore/Builder) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore)\ +[Cuemon.Extensions.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IApplicationBuilder|⬇️|`UseHostingEnvironment`, `UseCorrelationIdentifier`, `UseRequestIdentifier`, `UseUserAgentSentinel`, `UseThrottlingSentinel`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md index 6823df078..5499ad3d6 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md @@ -2,4 +2,24 @@ uid: Cuemon.Extensions.AspNetCore.Data.Integrity summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Data.Integrity namespace contains extension methods that complements the Cuemon.Data.Integrity namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.Data.Integrity namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Data.Integrity.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore/Data/Integrity)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore/Data/Integrity)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore/Data/Integrity) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore)\ +[Cuemon.Extensions.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|CacheValidator|⬇️|`ToEntityTag`| +|ChecksumBuilder|⬇️|`ToEntityTagHeaderValue`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md index 524302326..3958e88fb 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md @@ -2,4 +2,23 @@ uid: Cuemon.Extensions.AspNetCore.Http.Throttling summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Http.Throttling namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Http.Throttling namespace. Provides an in-memory implementation of a throttling cache for ASP.NET Core. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.AspNetCore.Http.Throttling namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Http.Throttling.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore/Http/Throttling)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore/Http/Throttling)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore/Http/Throttling) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore)\ +[Cuemon.Extensions.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IServiceCollection|⬇️|`AddThrottlingCache{T}`, `AddMemoryThrottlingCache`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md index eb1c9052d..c88351fc3 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md @@ -2,4 +2,26 @@ uid: Cuemon.Extensions.AspNetCore.Http summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore.Http namespace contains extension methods that complements the Cuemon.AspNetCore.Http namespace while being an addition to the Microsoft.AspNetCore.Http namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.AspNetCore.Http namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Http.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore/Http)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore/Http)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore/Http) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore)\ +[Cuemon.Extensions.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IHeaderDictionary|⬇️|`AddOrUpdateHeaders`, `AddOrUpdateHeader`| +|HttpRequest|⬇️|`IsGetOrHeadMethod`, `IsClientSideResourceCached`| +|HttpResponse|⬇️|`AddOrUpdateEntityTagHeader`, `AddOrUpdateLastModifiedHeader`, `WriteBodyAsync`, `OnStartingInvokeTransformer`| +|Int32|⬇️|`IsInformationStatusCode`, `IsSuccessStatusCode`, `IsRedirectionStatusCode`, `IsNotModifiedStatusCode`, `IsClientErrorStatusCode`, `IsServerErrorStatusCode`| \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md index ac293f0bd..dfc96fcf1 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md @@ -2,7 +2,7 @@ uid: Cuemon.Extensions.AspNetCore.Mvc.Configuration summary: *content --- -The Cuemon.Extensions.AspNetCore.Mvc.Configuration namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Configuration namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides a set of different cache busting strategies for ASP.NET Core that can be easily customized. +The Cuemon.Extensions.AspNetCore.Mvc.Configuration namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Configuration namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides a set of different cache busting strategies for ASP.NET Core MVC that can be easily customized. Availability: NET Standard 2.0, NET Core 3.0 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md index 515c15635..c00f5f3f8 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md @@ -2,7 +2,7 @@ uid: Cuemon.Extensions.AspNetCore.Mvc summary: *content --- -The Cuemon.Extensions.AspNetCore.Mvc namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Mvc namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides a set of different cache busting strategies for ASP.NET Core that can be easily customized. +The Cuemon.Extensions.AspNetCore.Mvc namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Mvc namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides a set of different cache busting strategies for ASP.NET Core MVC that can be easily customized. Availability: NET Standard 2.0, NET Core 3.0 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md index 940e51514..7e9f24685 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md @@ -2,4 +2,17 @@ uid: Cuemon.Extensions.AspNetCore summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Extensions.AspNetCore namespace contains both types and extension methods that complements the Cuemon.AspNetCore namespace while being an addition to the Microsoft.AspNetCore namespace. Provides an in-memory implementation of a throttling cache for ASP.NET Core. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Cuemon.AspNetCore namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.AspNetCore)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.AspNetCore)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.AspNetCore) + +NuGet packages 📦\ +[Cuemon.Extensions.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.AspNetCore)\ +[Cuemon.Extensions.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.AspNetCore) \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj index f05ada8fd..7c76b109a 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.AspNetCore.Mvc Cuemon.Extensions.AspNetCore.Mvc - The Cuemon.Extensions.AspNetCore.Mvc namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Mvc namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides a set of different cache busting strategies for ASP.NET Core that can be easily customized. + The Cuemon.Extensions.AspNetCore.Mvc namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Mvc namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides a set of different cache busting strategies for ASP.NET Core MVC that can be easily customized. extension-methods extensions add-assembly-cache-busting add-cache-busting add-dynamic-cache-busting use-when make-cacheable diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt index 24d260885..01ac1d0a0 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt @@ -2,11 +2,8 @@ Availability: NET Standard 2.0, NET Core 3.0   # Breaking Changes -- RENAMED ToCacheableObjectResult{T} --> MakeCacheable{T} on the CacheableObjectResultExtensions class in the Cuemon.Extensions.AspNetCore.Mvc (also included a non-generic variant: MakeCacheable) +- RENAMED ToCacheableObjectResult{T} --> MakeCacheable{T} on the CacheableObjectResultExtensions class in the Cuemon.Extensions.AspNetCore.Mvc namespace (also included a non-generic variant: MakeCacheable)   # New Features - ADDED ServiceCollectionExtensions class in the Cuemon.Extensions.AspNetCore.Mvc.Configuration namespace that consist of extension methods for the IServiceCollection interface: AddAssemblyCacheBusting, AddDynamicCacheBusting, AddCacheBusting{T} -  -# Improvements -- COMPATIBLE with the changes applied to NET Core 3 in regards to only allowing asynchronous I/O   \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj b/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj index 1b60f8643..f6669960c 100644 --- a/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj +++ b/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.AspNetCore Cuemon.Extensions.AspNetCore - The Cuemon.Extensions.AspNetCore namespace contains extension methods and features related to the Cuemon.AspNetCore namespace. + The Cuemon.Extensions.AspNetCore namespace contains both types and extension methods that complements the Cuemon.AspNetCore namespace while being an addition to the Microsoft.AspNetCore namespace. Provides an in-memory implementation of a throttling cache for ASP.NET Core. extension-methods extensions memory-throttling-cache use-hosting-environment-header use-correlation-identifier-header use-request-identifier-header use-user-agent-sentinel use-custom-throttling-sentinel diff --git a/src/Cuemon.Extensions.AspNetCore/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.AspNetCore/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..641c7cccf --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore/Properties/PackageReleaseNotes.txt @@ -0,0 +1,23 @@ +Version: 6.0.0 +Availability: NET Standard 2.0, NET Core 3.0 +  +# Upgrade Steps +- HttpResponseMessageExtensions class was not merged to this assembly +  +# Breaking Changes +- RENAMED UseHostingEnvironmentHeader --> UseHostingEnvironment on the ApplicationBuilderExtensions class in the Cuemon.Extensions.AspNetCore.Builder namespace +- RENAMED UseCorrelationIdentifierHeader --> UseCorrelationIdentifier on the ApplicationBuilderExtensions class in the Cuemon.Extensions.AspNetCore.Builder namespace +- RENAMED UseRequestIdentifierHeader --> UseRequestIdentifier on the ApplicationBuilderExtensions class in the Cuemon.Extensions.AspNetCore.Builder namespace +- RENAMED UseCustomThrottlingSentinel --> UseThrottlingSentinel on the ApplicationBuilderExtensions class in the Cuemon.Extensions.AspNetCore.Builder namespace +- RENAMED ToEntityTag --> ToEntityTagHeaderValue on the ChecksumBuilderExtensions class in the Cuemon.Extensions.AspNetCore.Data.Integrity namespace +- RENAMED AddMemoryThrottling --> AddMemoryThrottlingCache on the ServiceCollectionExtensions class in the Cuemon.Extensions.AspNetCore.Http.Throttling namespace +- RENAMED SetEntityTagHeaderInformation --> AddOrUpdateEntityTagHeader on the HttpResponseExtensions class in the Cuemon.Extensions.AspNetCore.Http namespace +- RENAMED SetLastModifiedHeaderInformation --> AddOrUpdateLastModifiedHeader on the HttpResponseExtensions class in the Cuemon.Extensions.AspNetCore.Http namespace +- REMOVED IsSuccessStatusCode from the HttpResponseExtensions class in the Cuemon.Extensions.AspNetCore.Http namespace +- REMOVED IsNotModifiedStatusCode from the HttpResponseExtensions class in the Cuemon.Extensions.AspNetCore.Http namespace +  +# New Features +- EXTENDED ServiceCollectionExtensions class in the Cuemon.Extensions.AspNetCore.Http.Throttling namespace with one new extension method for the IServiceCollection interface: AddThrottlingCache{T} +- EXTENDED HttpResponseExtensions class in the Cuemon.Extensions.AspNetCore.Http namespace with one new extension method for the HttpResponse class: OnStartingInvokeTransformer +- ADDED Int32Extensions class in the Cuemon.Extensions.AspNetCore.Http namespace that consist of extension methods for the Int32 struct: IsInformationStatusCode, IsSuccessStatusCode, IsRedirectionStatusCode, IsNotModifiedStatusCode, IsClientErrorStatusCode, IsServerErrorStatusCode +  \ No newline at end of file From 97462c2fa4e6564fe7e12a375635e5be041174fc Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 4 Oct 2020 21:26:03 +0200 Subject: [PATCH 267/385] Added ResourceAttribute while removing IMessageLocalizer. Applied quality action https://rules.sonarsource.com/csharp/RSPEC-3776. --- src/Cuemon.Core/ExceptionInsights.cs | 11 +++- .../Globalization/IMessageLocalizer.cs | 28 ---------- .../Globalization/ResourceAttribute.cs | 56 +++++++++++++++++++ .../Properties/PackageReleaseNotes.txt | 3 +- 4 files changed, 66 insertions(+), 32 deletions(-) delete mode 100644 src/Cuemon.Core/Globalization/IMessageLocalizer.cs create mode 100644 src/Cuemon.Core/Globalization/ResourceAttribute.cs diff --git a/src/Cuemon.Core/ExceptionInsights.cs b/src/Cuemon.Core/ExceptionInsights.cs index 759f4bf53..32f0e6b3e 100644 --- a/src/Cuemon.Core/ExceptionInsights.cs +++ b/src/Cuemon.Core/ExceptionInsights.cs @@ -56,7 +56,7 @@ public static T Embed(T exception, MethodBase thrower, object[] runtimeParame { var rp = DelimitedString.Create(MethodDescriptor.MergeParameters(descriptor, runtimeParameters), o => { - o.StringConverter = pair => FormattableString.Invariant($"{pair.Key}={pair.Value}"); + o.StringConverter = pair => FormattableString.Invariant($"{pair.Key}={pair.Value ?? "null"}"); o.Delimiter = FormattableString.Invariant($"{Alphanumeric.NewLine}"); }); builder.Append(Convert.ToBase64String(Convertible.GetBytes(FormattableString.Invariant($"{rp}")))); @@ -72,6 +72,13 @@ public static T Embed(T exception, MethodBase thrower, object[] runtimeParame builder.Append("."); builder.Append(empty); } + EmbedSystemSnapshot(builder, snapshot, empty); + if (exception.Data[Key] == null) { exception.Data.Add(Key, builder.ToString()); } + return exception; + } + + private static void EmbedSystemSnapshot(StringBuilder builder, SystemSnapshot snapshot, string empty) + { builder.Append("."); if (snapshot.HasFlag(SystemSnapshot.CaptureThreadInfo)) { @@ -102,8 +109,6 @@ public static T Embed(T exception, MethodBase thrower, object[] runtimeParame { builder.Append(empty); } - if (exception.Data[Key] == null) { exception.Data.Add(Key, builder.ToString()); } - return exception; } } } \ No newline at end of file diff --git a/src/Cuemon.Core/Globalization/IMessageLocalizer.cs b/src/Cuemon.Core/Globalization/IMessageLocalizer.cs deleted file mode 100644 index 727e75a9f..000000000 --- a/src/Cuemon.Core/Globalization/IMessageLocalizer.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace Cuemon.Globalization -{ - /// - /// Provides a generic way to support localized messages on attribute decorated methods. - /// - public interface IMessageLocalizer - { - /// - /// Gets or sets and explicit message string. - /// - /// This property is intended to be used for non-localizable messages. Use and for localizable messages. - string Message { get; set; } - - /// - /// Gets or sets the resource name (property name) to use as the key for lookups on the resource type. - /// - /// Use this property to set the name of the property within that will provide a localized message. - string MessageResourceName { get; set; } - - /// - /// Gets or sets the resource type to use for message lookups. - /// - /// Use this property only in conjunction with . They are used together to retrieve localized messages at runtime. - Type MessageResourceType { get; set; } - } -} \ No newline at end of file diff --git a/src/Cuemon.Core/Globalization/ResourceAttribute.cs b/src/Cuemon.Core/Globalization/ResourceAttribute.cs new file mode 100644 index 000000000..deb787904 --- /dev/null +++ b/src/Cuemon.Core/Globalization/ResourceAttribute.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Concurrent; +using System.Reflection; + +namespace Cuemon.Globalization +{ + /// + /// Provides a generic way to support localization on attribute decorated methods. + /// + /// + public abstract class ResourceAttribute : Attribute + { + private readonly ConcurrentDictionary _propertyInfos = new ConcurrentDictionary(); + + /// + /// Initializes a new instance of the class. + /// + protected ResourceAttribute() + { + } + + /// + /// Gets or sets the type that contains the resources for looking up localized strings. + /// + /// The type that contains the resources for looking up localized strings. + public Type ResourceType { get; set; } + + /// + /// Returns the value of the specified string resource. + /// + /// The name of the resource to retrieve. + /// The value of the resource localized for the callers current UI culture, or null if cannot be found on the . + /// + /// You must specify a to perform the actual lookup of localized strings. + /// + /// + /// The specified does not contain a resource with the specified . + /// + protected string GetString(string name) + { + Validator.ThrowIfNullOrWhitespace(name, nameof(name)); + if (ResourceType == null) { throw new InvalidOperationException("You must specify a type to perform the actual lookup of localized strings."); } + var cacheKey = $"{ResourceType.ToString().ToUpperInvariant()}.{name.ToUpperInvariant()}"; + if (!_propertyInfos.TryGetValue(cacheKey, out var property)) + { + property = ResourceType.GetProperty(name, BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic); + var getMethod = property?.GetGetMethod(true); + if (getMethod != null && (getMethod.IsAssembly || getMethod.IsPublic)) + { + _propertyInfos.TryAdd(cacheKey, property); + } + } + return property == null ? throw new ArgumentException($"The specified type, '{ResourceType.FullName}', does not contain a resource with the specified '{name}'.") : property.GetValue(null, null) as string; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt index b7ad227f2..a58e7582b 100644 --- a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt @@ -28,9 +28,10 @@ Availability: NET Standard 2.0 - MOVED TransientFaultException class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) - MOVED TransientOperation class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) - MOVED TransientOperationOptions class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- REMOVED IMessageLocalizer interface from the Cuemon.Globalization namespace   # New Features -- +- ADDED ResourceAttribute class in the Cuemon.Globalization namespace that provides a generic way to support localization on attribute decorated methods -   # Bug Fixes From 614f1c007357538053ff85e0ba436f426163ead8 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 4 Oct 2020 21:27:03 +0200 Subject: [PATCH 268/385] Simplified ExceptionDescriptorAttribute. --- .../ExceptionDescriptorAttribute.cs | 43 ++++++++----------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/Cuemon.Diagnostics/ExceptionDescriptorAttribute.cs b/src/Cuemon.Diagnostics/ExceptionDescriptorAttribute.cs index c01fd6d0f..443981e09 100644 --- a/src/Cuemon.Diagnostics/ExceptionDescriptorAttribute.cs +++ b/src/Cuemon.Diagnostics/ExceptionDescriptorAttribute.cs @@ -1,5 +1,4 @@ using System; -using System.Reflection; using Cuemon.Globalization; namespace Cuemon.Diagnostics @@ -9,9 +8,10 @@ namespace Cuemon.Diagnostics /// /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class ExceptionDescriptorAttribute : Attribute, IMessageLocalizer + public class ExceptionDescriptorAttribute : ResourceAttribute { private string _helpLink; + private string _message; /// /// Initializes a new instance of the class. @@ -22,21 +22,6 @@ public ExceptionDescriptorAttribute(Type failureType) Validator.ThrowIfNull(failureType, nameof(failureType)); Validator.ThrowIfNotContainsType(failureType, nameof(failureType), "The specified type is not an Exception.", typeof(Exception)); FailureType = failureType; - Message = InitializeFromResource(); - } - - private string InitializeFromResource() - { - if (!string.IsNullOrWhiteSpace(MessageResourceName) && MessageResourceType != null) - { - var property = MessageResourceType.GetProperty(MessageResourceName, BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic); - var getMethod = property?.GetGetMethod(true); - if (getMethod != null && (getMethod.IsAssembly || getMethod.IsPublic)) - { - return property.GetValue(null, null) as string; - } - } - return null; } /// @@ -49,20 +34,26 @@ private string InitializeFromResource() /// Gets or sets a default message that describes the current failure. /// /// The default message that explains the reason for the failure. - public string Message { get; set; } + public string Message + { + get + { + string localizedMessage = null; + if (ResourceType != null) + { + localizedMessage = GetString(MessageResourceName); + } + return localizedMessage ?? _message; + } + set => _message = value; + } /// - /// Gets or sets the resource name (property name) to use as the key for lookups on the resource type. + /// Gets or sets the resource name (property name) to use as the key for looking up a localized message string. /// - /// Use this property to set the name of the property within that will provide a localized message that describes the current failure. + /// The resource name (property name) to use as the key for looking up a localized message string that describes the current failure. public string MessageResourceName { get; set; } - /// - /// Gets or sets the resource type to use for message lookups. - /// - /// Use this property only in conjunction with . They are used together to retrieve localized messages at runtime. - public Type MessageResourceType { get; set; } - /// /// Gets or sets a link to the help page associated with this failure. /// From fdc7c514bdd140809452d08ace497e93aaa53bed Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 4 Oct 2020 21:27:53 +0200 Subject: [PATCH 269/385] Minor improvements to ExceptionDescriptor and added long overdue unit testing. --- .../Cuemon.Diagnostics.csproj | 3 +- src/Cuemon.Diagnostics/ExceptionDescriptor.cs | 16 +- ...rgumentNullExceptionDescriptorAttribute.cs | 13 ++ .../Assets/SomeClass.cs | 29 +++ .../Cuemon.Diagnostics.Tests.csproj | 16 ++ .../ExceptionDescriptorTest.cs | 192 ++++++++++++++++++ .../TestContext.Designer.cs | 72 +++++++ .../TestContext.da.resx | 123 +++++++++++ .../Cuemon.Diagnostics.Tests/TestContext.resx | 123 +++++++++++ 9 files changed, 585 insertions(+), 2 deletions(-) create mode 100644 test/Cuemon.Diagnostics.Tests/Assets/ArgumentNullExceptionDescriptorAttribute.cs create mode 100644 test/Cuemon.Diagnostics.Tests/Assets/SomeClass.cs create mode 100644 test/Cuemon.Diagnostics.Tests/ExceptionDescriptorTest.cs create mode 100644 test/Cuemon.Diagnostics.Tests/TestContext.Designer.cs create mode 100644 test/Cuemon.Diagnostics.Tests/TestContext.da.resx create mode 100644 test/Cuemon.Diagnostics.Tests/TestContext.resx diff --git a/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj b/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj index 7277cb46a..059f9c2f0 100644 --- a/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj +++ b/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj @@ -9,7 +9,8 @@ Cuemon.Diagnostics Cuemon.Diagnostics The Cuemon.Diagnostics namespace contains features that extends the System.Diagnostics namespace. - time-measuring async-time-measuring profiler exception-descriptor + The Cuemon.Diagnostics namespace contains types that provide ways for developers to describe exceptions including evidence to why an operation faulted. Also includes a flexible, generic and lambda friendly way to perform both synchronous and asynchronous time measuring operations. The namespace is an addition to the System.Diagnostics namespace. + time-measuring time-measure time-measure-profiler with-action with-func async-time-measuring with-action-async with-func-async profiler exception-descriptor diff --git a/src/Cuemon.Diagnostics/ExceptionDescriptor.cs b/src/Cuemon.Diagnostics/ExceptionDescriptor.cs index 1f6e674ac..ffde3ead7 100644 --- a/src/Cuemon.Diagnostics/ExceptionDescriptor.cs +++ b/src/Cuemon.Diagnostics/ExceptionDescriptor.cs @@ -44,7 +44,12 @@ public static ExceptionDescriptor Extract(Exception exception, string code = "Un builder.AppendLine(exception.ToString()); var memberSignature = Convertible.ToString(Convert.FromBase64String(insights[IndexOfThrower])); var runtimeParameters = Convertible.ToString(Convert.FromBase64String(insights[IndexOfRuntimeParameters])); - ed.AddEvidence("Thrower", new MemberEvidence(memberSignature, string.IsNullOrWhiteSpace(runtimeParameters) ? null : runtimeParameters.Split(Alphanumeric.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToDictionary(k => k.Substring(0, k.IndexOf('=')), v => v.Substring(v.IndexOf('=') + 1))), evidence => evidence); + ed.AddEvidence("Thrower", new MemberEvidence(memberSignature, string.IsNullOrWhiteSpace(runtimeParameters) ? null : runtimeParameters.Split(Alphanumeric.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToDictionary(k => k.Substring(0, k.IndexOf('=')), v => + { + var t = v.Substring(v.IndexOf('=') + 1); + if (t == "null") { return null; } + return t; + })), evidence => evidence); TryAddEvidence(ed, "Thread", insights[IndexOfThreadInfo]); TryAddEvidence(ed, "Process", insights[IndexOfProcessInfo]); TryAddEvidence(ed, "Environment", insights[IndexOfEnvironmentInfo]); @@ -176,5 +181,14 @@ public void PostInitializeWith(IEnumerable attribu if (!string.IsNullOrWhiteSpace(attribute.HelpLink)) { HelpLink = new Uri(attribute.HelpLink); } } } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + return Failure.ToString(); + } } } \ No newline at end of file diff --git a/test/Cuemon.Diagnostics.Tests/Assets/ArgumentNullExceptionDescriptorAttribute.cs b/test/Cuemon.Diagnostics.Tests/Assets/ArgumentNullExceptionDescriptorAttribute.cs new file mode 100644 index 000000000..445407c3d --- /dev/null +++ b/test/Cuemon.Diagnostics.Tests/Assets/ArgumentNullExceptionDescriptorAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Cuemon.Diagnostics.Assets +{ + public sealed class ArgumentNullExceptionDescriptorAttribute : ExceptionDescriptorAttribute + { + public ArgumentNullExceptionDescriptorAttribute() : base(typeof(ArgumentNullException)) + { + Code = "ArgumentNullException"; + Message = TestContext.FaultDescriptor_ArgumentNullException; + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Diagnostics.Tests/Assets/SomeClass.cs b/test/Cuemon.Diagnostics.Tests/Assets/SomeClass.cs new file mode 100644 index 000000000..97eded2bc --- /dev/null +++ b/test/Cuemon.Diagnostics.Tests/Assets/SomeClass.cs @@ -0,0 +1,29 @@ +using System; +using Cuemon.Extensions.Collections.Generic; + +namespace Cuemon.Diagnostics.Assets +{ + public class SomeClass + { + [ArgumentNullExceptionDescriptor] + public string[] StringToArray(string value) + { + Validator.ThrowIfNull(value, nameof(value), "Null is a no-go!"); + return value.Split(','); + } + + [ExceptionDescriptor(typeof(ArgumentNullException), Code = "ArgumentNullException", Message = "The value cannot be null (none-resource).", MessageResourceName = "FaultDescriptor_ArgumentNullException", ResourceType = typeof(TestContext))] + public string Shuffle(string value) + { + Validator.ThrowIfNull(value, nameof(value), "Null is a no-go!"); + return string.Concat(value.Shuffle()); + } + + [ExceptionDescriptor(typeof(ArgumentNullException), Code = "ArgumentNullException", Message = "The value cannot be null (none-resource).")] + public string ShuffleNoLoc(string value) + { + Validator.ThrowIfNull(value, nameof(value), "Null is a no-go!"); + return string.Concat(value.Shuffle()); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Diagnostics.Tests/Cuemon.Diagnostics.Tests.csproj b/test/Cuemon.Diagnostics.Tests/Cuemon.Diagnostics.Tests.csproj index 15b7d82c5..92bf21970 100644 --- a/test/Cuemon.Diagnostics.Tests/Cuemon.Diagnostics.Tests.csproj +++ b/test/Cuemon.Diagnostics.Tests/Cuemon.Diagnostics.Tests.csproj @@ -6,6 +6,22 @@ + + + + + + True + True + TestContext.resx + + + + + + ResXFileCodeGenerator + TestContext.Designer.cs + \ No newline at end of file diff --git a/test/Cuemon.Diagnostics.Tests/ExceptionDescriptorTest.cs b/test/Cuemon.Diagnostics.Tests/ExceptionDescriptorTest.cs new file mode 100644 index 000000000..1005f7f1a --- /dev/null +++ b/test/Cuemon.Diagnostics.Tests/ExceptionDescriptorTest.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using Cuemon.Collections.Generic; +using Cuemon.Diagnostics.Assets; +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Diagnostics +{ + public class ExceptionDescriptorTest : Test + { + public ExceptionDescriptorTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Extract_VerifyThatValidatorProvideInsights() + { + Stream someObject = null; + var enrichedException = Assert.Throws(() => Validator.ThrowIfNull(someObject, nameof(someObject))); + var ed = ExceptionDescriptor.Extract(enrichedException); + + Assert.Equal(enrichedException.ToString(), ed.ToString()); + Assert.Equal("UnhandledException", ed.Code); + Assert.Equal("An unhandled exception occurred.", ed.Message); + var me = Assert.Single(ed.Evidence).Value as MemberEvidence; + Assert.Equal(me.MemberSignature, "Cuemon.Validator.ThrowIfNull(T value, String paramName, String message)"); + Assert.Equal(3, me.RuntimeParameters.Count); + Assert.True(me.RuntimeParameters.ContainsKey("value")); + Assert.True(me.RuntimeParameters.ContainsKey("paramName")); + Assert.True(me.RuntimeParameters.ContainsKey("message")); + Assert.Null(me.RuntimeParameters["value"]); + Assert.Equal(nameof(someObject), me.RuntimeParameters["paramName"]); + Assert.Equal("Value cannot be null.", me.RuntimeParameters["message"]); + } + + [Fact] + public void Extract_VerifyThatInlineExceptionIncludesSystemSnapshot() + { + var ane = new ArgumentNullException("myParam", "myMessage"); + var enrichedException = ExceptionInsights.Embed(ane, MethodBase.GetCurrentMethod(), Arguments.ToArray(null, "myParam", "myMessage"), SystemSnapshot.CaptureAll); + var ed = ExceptionDescriptor.Extract(enrichedException); + + Assert.Equal(enrichedException.ToString(), ed.ToString()); + Assert.Equal("UnhandledException", ed.Code); + Assert.Equal("An unhandled exception occurred.", ed.Message); + Assert.Equal(4, ed.Evidence.Count); + var me = ed.Evidence.Single(pair => pair.Key == "Thrower") .Value as MemberEvidence; + Assert.Equal(me.MemberSignature, "Cuemon.Diagnostics.ExceptionDescriptorTest.Extract_VerifyThatInlineExceptionIncludesSystemSnapshot()"); + Assert.Equal(3, me.RuntimeParameters.Count); + Assert.True(me.RuntimeParameters.ContainsKey("arg1")); + Assert.True(me.RuntimeParameters.ContainsKey("arg2")); + Assert.True(me.RuntimeParameters.ContainsKey("arg3")); + Assert.Null(me.RuntimeParameters["arg1"]); + Assert.Equal("myParam", me.RuntimeParameters["arg2"]); + Assert.Equal("myMessage", me.RuntimeParameters["arg3"]); + var ti = ed.Evidence.Single(pair => pair.Key == "Thread").Value as IDictionary; + Assert.NotNull(ti); + Assert.True(ti.Count > 0); + TestOutput.WriteLine(DelimitedString.Create(ti, o => o.Delimiter = Environment.NewLine)); + var pi = ed.Evidence.Single(pair => pair.Key == "Process").Value as IDictionary; + Assert.NotNull(pi); + Assert.True(pi.Count > 0); + TestOutput.WriteLine(DelimitedString.Create(pi, o => o.Delimiter = Environment.NewLine)); + var ei = ed.Evidence.Single(pair => pair.Key == "Environment").Value as IDictionary; + Assert.NotNull(ei); + Assert.True(ei.Count > 0); + TestOutput.WriteLine(DelimitedString.Create(ei, o => o.Delimiter = Environment.NewLine)); + } + + [Fact] + public void ShouldCreateDefaultInstance() + { + var hu = new Uri("https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.ExceptionDescriptor.html"); + var ex = new InvalidOperationException("Invalid operation test."); + var ed = new ExceptionDescriptor(ex, "Invalid Operation Exception", "Developer did something unexpected.", hu); + + Assert.Equal(ex.ToString(), ed.ToString()); + Assert.Equal("InvalidOperationException", ed.Code); + Assert.Equal("Developer did something unexpected.", ed.Message); + Assert.Equal(hu, ed.HelpLink); + Assert.Equal(0, ed.Evidence.Count); + Assert.Equal(ex, ed.Failure); + } + + [Fact] + public void ShouldCreateDefaultInstanceWithPostInitializeUsingCustomImplementedExceptionDescriptorAttribute() + { + var sc = new SomeClass(); + var hu = new Uri("https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.ExceptionDescriptor.html"); + var ex = Assert.Throws(() => sc.StringToArray(null)); + var ed = new ExceptionDescriptor(ex, "Not Null Exception", "Null is not allowed.", hu); + + Assert.Equal(ex.Message, "Null is a no-go! (Parameter 'value')"); + Assert.Equal(ex.ToString(), ed.ToString()); + Assert.Equal("NotNullException", ed.Code); + Assert.Equal("Null is not allowed.", ed.Message); + Assert.Equal(hu, ed.HelpLink); + Assert.Equal(0, ed.Evidence.Count); + Assert.Equal(ex, ed.Failure); + + Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + + ed.PostInitializeWith(sc.GetType().GetMethod("StringToArray").GetCustomAttribute()); + + Assert.Equal("ArgumentNullException", ed.Code); + Assert.NotEqual("The value cannot be null (none-resource).", ed.Message); + Assert.Equal("Value cannot be null.", ed.Message); + + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("da-DK"); + + ed.PostInitializeWith(sc.GetType().GetMethod("StringToArray").GetCustomAttribute()); + + Assert.Equal("ArgumentNullException", ed.Code); + Assert.NotEqual("Value cannot be null.", ed.Message); + Assert.Equal("Null er ikke en gyldig værdi.", ed.Message); + } + + [Fact] + public void ShouldCreateDefaultInstanceWithPostInitializeUsingExceptionDescriptorAttributeWithLocalization() + { + var sc = new SomeClass(); + var hu = new Uri("https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.ExceptionDescriptor.html"); + var ex = Assert.Throws(() => sc.Shuffle(null)); + var ed = new ExceptionDescriptor(ex, "Not Null Exception", "Null is not allowed.", hu); + + Assert.Equal(ex.Message, "Null is a no-go! (Parameter 'value')"); + Assert.Equal(ex.ToString(), ed.ToString()); + Assert.Equal("NotNullException", ed.Code); + Assert.Equal("Null is not allowed.", ed.Message); + Assert.Equal(hu, ed.HelpLink); + Assert.Equal(0, ed.Evidence.Count); + Assert.Equal(ex, ed.Failure); + + Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + + ed.PostInitializeWith(sc.GetType().GetMethod("Shuffle").GetCustomAttribute()); + + Assert.Equal("ArgumentNullException", ed.Code); + Assert.NotEqual("The value cannot be null (none-resource).", ed.Message); + Assert.Equal("Value cannot be null.", ed.Message); + + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("da-DK"); + + ed.PostInitializeWith(sc.GetType().GetMethod("Shuffle").GetCustomAttribute()); + + Assert.Equal("ArgumentNullException", ed.Code); + Assert.NotEqual("Value cannot be null.", ed.Message); + Assert.Equal("Null er ikke en gyldig værdi.", ed.Message); + } + + [Fact] + public void ShouldCreateDefaultInstanceWithPostInitializeUsingExceptionDescriptorAttributeWithoutLocalization() + { + var sc = new SomeClass(); + var hu = new Uri("https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.ExceptionDescriptor.html"); + var ex = Assert.Throws(() => sc.ShuffleNoLoc(null)); + var ed = new ExceptionDescriptor(ex, "Not Null Exception", "Null is not allowed.", hu); + + Assert.Equal(ex.Message, "Null is a no-go! (Parameter 'value')"); + Assert.Equal(ex.ToString(), ed.ToString()); + Assert.Equal("NotNullException", ed.Code); + Assert.Equal("Null is not allowed.", ed.Message); + Assert.Equal(hu, ed.HelpLink); + Assert.Equal(0, ed.Evidence.Count); + Assert.Equal(ex, ed.Failure); + + Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + + ed.PostInitializeWith(sc.GetType().GetMethod("ShuffleNoLoc").GetCustomAttribute()); + + Assert.Equal("ArgumentNullException", ed.Code); + Assert.Equal("The value cannot be null (none-resource).", ed.Message); + Assert.NotEqual("Value cannot be null.", ed.Message); + + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("da-DK"); + + ed.PostInitializeWith(sc.GetType().GetMethod("ShuffleNoLoc").GetCustomAttribute()); + + Assert.Equal("ArgumentNullException", ed.Code); + Assert.Equal("The value cannot be null (none-resource).", ed.Message); + Assert.NotEqual("Null er ikke en gyldig værdi.", ed.Message); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Diagnostics.Tests/TestContext.Designer.cs b/test/Cuemon.Diagnostics.Tests/TestContext.Designer.cs new file mode 100644 index 000000000..a59d6b198 --- /dev/null +++ b/test/Cuemon.Diagnostics.Tests/TestContext.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Cuemon.Diagnostics { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class TestContext { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal TestContext() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Cuemon.Diagnostics.TestContext", typeof(TestContext).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Value cannot be null.. + /// + internal static string FaultDescriptor_ArgumentNullException { + get { + return ResourceManager.GetString("FaultDescriptor_ArgumentNullException", resourceCulture); + } + } + } +} diff --git a/test/Cuemon.Diagnostics.Tests/TestContext.da.resx b/test/Cuemon.Diagnostics.Tests/TestContext.da.resx new file mode 100644 index 000000000..d1656eb7d --- /dev/null +++ b/test/Cuemon.Diagnostics.Tests/TestContext.da.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Null er ikke en gyldig værdi. + + \ No newline at end of file diff --git a/test/Cuemon.Diagnostics.Tests/TestContext.resx b/test/Cuemon.Diagnostics.Tests/TestContext.resx new file mode 100644 index 000000000..c28d91b4b --- /dev/null +++ b/test/Cuemon.Diagnostics.Tests/TestContext.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Value cannot be null. + + \ No newline at end of file From 162a1ad0915e2abe354fc66c9116e9a580d9c3f7 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sun, 4 Oct 2020 21:33:06 +0200 Subject: [PATCH 270/385] Updated package description, tags, release notes and DocFx namespace description. --- docfx/api/namespaces/Cuemon.Diagnostics.md | 17 ++++++++++++++++- .../Properties/PackageReleaseNotes.txt | 12 ++++++++++++ .../Properties/PackageReleaseNotes.txt | 12 ++++++++++++ .../Cuemon.Diagnostics.csproj | 3 +-- 4 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt create mode 100644 src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Diagnostics.md b/docfx/api/namespaces/Cuemon.Diagnostics.md index 5b0abea8e..33a65faa0 100644 --- a/docfx/api/namespaces/Cuemon.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.Diagnostics.md @@ -2,4 +2,19 @@ uid: Cuemon.Diagnostics summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Diagnostics namespace contains types that provide ways for developers to describe exceptions including evidence to why an operation faulted. Also includes a flexible, generic and lambda friendly way to perform both synchronous and asynchronous time measuring operations. The namespace is an addition to the System.Diagnostics namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Diagnostics namespace](https://docs.microsoft.com/en-us/dotnet/api/system.Diagnostics?view=netstandard-2.0) 🔗 + +Related: [Cuemon.Extensions.Diagnostics namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Diagnostics.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Diagnostics)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Diagnostics)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Diagnostics) + +NuGet packages 📦\ +[Cuemon.Diagnostics (CI)](https://nuget.cuemon.net/packages/Cuemon.Diagnostics)\ +[Cuemon.Diagnostics (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Diagnostics) \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt b/src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..026dadfe6 --- /dev/null +++ b/src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt @@ -0,0 +1,12 @@ +Version: 6.0.0 +Availability: NET Standard 2.0, NET Core 3.0 +  +# Upgrade Steps +- Any former extension methods of the Cuemon.AspNetCore.Mvc namespace was merged into the Cuemon.Extensions.AspNetCore.Mvc namespace +  +# Breaking Changes +- REMOVED DefaultJsonSerializerSettings class from the Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json namespace as the default settings is given by JsonFormatterOptions +  +# Improvements +- COMPATIBLE with the changes applied to NET Core 3 in regards to only allowing asynchronous I/O +  \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt b/src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..39a6de32a --- /dev/null +++ b/src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt @@ -0,0 +1,12 @@ +Version: 6.0.0 +Availability: NET Standard 2.0, NET Core 3.0 +  +# Upgrade Steps +- Any former extension methods of the Cuemon.AspNetCore namespace was merged into the Cuemon.Extensions.AspNetCore namespace +  +# Breaking Changes +- +  +# Improvements +- +  \ No newline at end of file diff --git a/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj b/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj index 059f9c2f0..3694c6c14 100644 --- a/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj +++ b/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -8,7 +8,6 @@ Cuemon.Diagnostics Cuemon.Diagnostics - The Cuemon.Diagnostics namespace contains features that extends the System.Diagnostics namespace. The Cuemon.Diagnostics namespace contains types that provide ways for developers to describe exceptions including evidence to why an operation faulted. Also includes a flexible, generic and lambda friendly way to perform both synchronous and asynchronous time measuring operations. The namespace is an addition to the System.Diagnostics namespace. time-measuring time-measure time-measure-profiler with-action with-func async-time-measuring with-action-async with-func-async profiler exception-descriptor From dc075b7572eaf7b3aa3a8a0842520ee74b2d668c Mon Sep 17 00:00:00 2001 From: gimlichael Date: Mon, 5 Oct 2020 22:20:52 +0200 Subject: [PATCH 271/385] Updated release notes. --- src/Cuemon.Core/Properties/PackageReleaseNotes.txt | 3 ++- .../Properties/PackageReleaseNotes.txt | 13 +++++++++++++ src/Cuemon.Net/Properties/PackageReleaseNotes.txt | 9 ++++----- .../Properties/PackageReleaseNotes.txt | 12 ++++++------ 4 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 src/Cuemon.Diagnostics/Properties/PackageReleaseNotes.txt diff --git a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt index a58e7582b..a022958b7 100644 --- a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt @@ -2,7 +2,8 @@ Availability: NET Standard 2.0   # Upgrade Steps -- To use the earlier built-in support for transient fault handling, please refer to the Cuemon.Resilience namespace, as it has been merged and refactored into this assembly +- To use the earlier built-in support for transient fault handling, please refer to the Cuemon.Resilience namespace, as it has been merged and refactored out of this assembly +- To use the earlier built-in support for time-measuring and describing exceptions, please refer to the Cuemon.Diagnostics namespace, as it has been merged and refactored out of this assembly - Any former extension methods of the Cuemon namespace (and related) was either removed completely or merged into there respective Cuemon.Extensions.* namespace equivalent   # Breaking Changes diff --git a/src/Cuemon.Diagnostics/Properties/PackageReleaseNotes.txt b/src/Cuemon.Diagnostics/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..ee56cc28e --- /dev/null +++ b/src/Cuemon.Diagnostics/Properties/PackageReleaseNotes.txt @@ -0,0 +1,13 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# Breaking Changes +- REMOVED EventLogEntryType enum from the Cuemon.Diagnostics namespace as it is now (finally) part of .NET Platform Extensions and .NET Core +- CHANGED ExceptionDescriptorAttribute class in the Cuemon.Diagnostics namespace to have a more simple and streamlined design +- CHANGED TimeMeasure class in the Cuemon.Diagnostics namespace to fully support the Task-based Asynchronous Pattern (TAP) with cancellation +  +# New Features +- ADDED ExceptionDescriptorOptions class into Cuemon.Diagnostics namespace that specifies configuration options for serializer implementations +  +# Improvements +- EXTENDED ExceptionDescriptor class in the Cuemon.Diagnostics namespace with a new static method: Extract \ No newline at end of file diff --git a/src/Cuemon.Net/Properties/PackageReleaseNotes.txt b/src/Cuemon.Net/Properties/PackageReleaseNotes.txt index cd8a1977d..48469e631 100644 --- a/src/Cuemon.Net/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Net/Properties/PackageReleaseNotes.txt @@ -6,15 +6,14 @@ Availability: NET Standard 2.0 - Any types found in the former Cuemon.Net.Mail namespace was merged into this assembly with and equivalent namespace - Any former extension methods of the Cuemon.Net namespace was merged into the Cuemon.Extensions.Net namespace   +# Breaking Changes +- REMOVED SetHandlerFactory{T} method on the HttpManagerOptions class (opt-in to allow set directly on HandlerFactory property) +  # New Features - ADDED MailDistributor class in the Cuemon.Net.Mail namespace that provides a way for applications to distribute one or more e-mails in batches by using the Simple Mail Transfer Protocol (SMTP) - ADDED FieldValueSeparator enum in the Cuemon.Net namespace that specifies a range of key-value separators - ADDED QueryStringCollection class in the Cuemon.Net namespace that provides a collection of string values that is equivalent to a query string of an Uri   -# Breaking Changes -- REMOVED SetHandlerFactory{T} method on the HttpManagerOptions class (opt-in to allow set directly on HandlerFactory property) -  # Improvements - ADDED HttpManager constructor overload that takes a client factory delegate which creates and configures an HttpClient instance -- CHANGED HttpManagerOptions default value for DisposeHandler from true to false. This is due to the way Microsoft has designed the HttpClient with an implementation of IDisposable that could result in SocketException errors if not instantiated once and re-used throughout the life of an application, This setting reduces the risk of SocketException errors on existing code -  \ No newline at end of file +- CHANGED HttpManagerOptions default value for DisposeHandler from true to false. This is due to the way Microsoft has designed the HttpClient with an implementation of IDisposable that could result in SocketException errors if not instantiated once and re-used throughout the life of an application, This setting reduces the risk of SocketException errors on existing code \ No newline at end of file diff --git a/src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt b/src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt index 85d9100bf..fc536eab9 100644 --- a/src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt @@ -5,14 +5,14 @@ Availability: NET Standard 2.0 - Some features (such as Memoization techniques and GetOrAdd convenience) was moved to the Cuemon.Extensions.Runtime.Caching namespace as extension methods (to keep the ICacheEnumerable{TKey} slim) - The extent of refactoring applied to this project resulted in so many breaking changes that a git diff is advisable   +# Breaking Changes +- CHANGED CachingManager class to return a singleton of SlimMemoryCache with default options (kept for legacy and convenience) +- REPLACED Cache class in the Cuemon.Runtime.Caching namespace with CacheEntry and split the cache invalidation part into its own class; CacheInvalidation +- REPLACED CacheCollection class in the Cuemon.Runtime.Caching namespace with SlimMemoryCache +  # New Features - ADDED CacheEntry class in the Cuemon.Runtime.Caching namespace that represents an individual cache entry in the cache - ADDED CacheInvalidation class in the Cuemon.Runtime.Caching namespace that represents a set of eviction and expiration details for a specific cache entry - ADDED ICacheEnumerable{TKey} interface in the Cuemon.Runtime.Caching namespace that is used to provide cache implementations for an application - ADDED SlimMemoryCache class in the Cuemon.Runtime.Caching namespace that represents the type that implements an in-memory cache for an application -- ADDED SlimMemoryCacheOptions class in the Cuemon.Runtime.Caching namespace that specifies options related to SlimMemoryCache -  -# Breaking Changes -- CHANGED CachingManager class to return a singleton of SlimMemoryCache with default options (kept for legacy and convenience) -- REPLACED Cache class in the Cuemon.Runtime.Caching namespace with CacheEntry and split the cache invalidation part into its own class; CacheInvalidation -- REPLACED CacheCollection class in the Cuemon.Runtime.Caching namespace with SlimMemoryCache \ No newline at end of file +- ADDED SlimMemoryCacheOptions class in the Cuemon.Runtime.Caching namespace that specifies options related to SlimMemoryCache \ No newline at end of file From df20390ef0a04859b405c1f6f0a1d5af16521fd9 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 7 Oct 2020 00:41:12 +0200 Subject: [PATCH 272/385] Updated package description, release notes and refactored GetCommandCore. --- .../Cuemon.Data.SqlClient.csproj | 1 + .../Properties/PackageReleaseNotes.txt | 6 ++ src/Cuemon.Data.SqlClient/SqlDataManager.cs | 84 ++++++++++--------- src/Cuemon.Data/DataManager.cs | 2 +- 4 files changed, 52 insertions(+), 41 deletions(-) create mode 100644 src/Cuemon.Data.SqlClient/Properties/PackageReleaseNotes.txt diff --git a/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj b/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj index bd571a6ca..221b9aa90 100644 --- a/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj +++ b/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj @@ -8,6 +8,7 @@ Cuemon.Data.SqlClient Cuemon.Data.SqlClient + The Cuemon.Data.SqlClient namespace contains types that provide ways for developers to work with Microsoft SQL Server integrations. Also includes a flexible, generic and lambda friendly way to perform both synchronous and asynchronous time measuring operations. The namespace is an addition to the System.Diagnostics namespace. The Cuemon.Data.SqlClient namespace contains different Microsoft SQL Server implementations of different abstractions found in the Cuemon.Data namespace. sql sql-in-operator sql-query-builder sql-data-manager transient-fault-handling diff --git a/src/Cuemon.Data.SqlClient/Properties/PackageReleaseNotes.txt b/src/Cuemon.Data.SqlClient/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..d083e92b9 --- /dev/null +++ b/src/Cuemon.Data.SqlClient/Properties/PackageReleaseNotes.txt @@ -0,0 +1,6 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# Breaking Changes +- CHANGED SqlDataManager class in the Cuemon.Data.SqlClient namespace to be less dependant on base class and applied quality gate actions +- CHANGED SqlInOperator class in the Cuemon.Data.SqlClient namespace to adapt the changes made to the base class \ No newline at end of file diff --git a/src/Cuemon.Data.SqlClient/SqlDataManager.cs b/src/Cuemon.Data.SqlClient/SqlDataManager.cs index c2c8d2494..e820327ed 100644 --- a/src/Cuemon.Data.SqlClient/SqlDataManager.cs +++ b/src/Cuemon.Data.SqlClient/SqlDataManager.cs @@ -1,9 +1,11 @@ using System; +using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Globalization; using System.Linq; +using System.Reflection; using Cuemon.Collections.Generic; using Cuemon.Resilience; @@ -108,9 +110,9 @@ public override int ExecuteIdentityInt32(IDataCommand dataCommand, params DbPara if (dataCommand == null) throw new ArgumentNullException(nameof(dataCommand)); if (dataCommand.Type != CommandType.Text) { throw new ArgumentException("This method only supports CommandType.Text specifications.", nameof(dataCommand)); } return ExecuteScalarAsInt32(new DataCommand(FormattableString.Invariant($"{dataCommand.Text} SELECT CONVERT(INT, SCOPE_IDENTITY())")) - { - Timeout = dataCommand.Timeout - }, parameters); + { + Timeout = dataCommand.Timeout + }, parameters); } /// @@ -174,8 +176,8 @@ public override DataManager Clone() /// protected override T ExecuteCore(IDataCommand dataCommand, DbParameter[] parameters, Func commandInvoker) { - return TransientFaultHandlingOptionsCallback == null - ? base.ExecuteCore(dataCommand, parameters, commandInvoker) + return TransientFaultHandlingOptionsCallback == null + ? base.ExecuteCore(dataCommand, parameters, commandInvoker) : TransientOperation.WithFunc(() => base.ExecuteCore(dataCommand, parameters, commandInvoker), TransientFaultHandlingOptionsCallback); } @@ -184,49 +186,51 @@ protected override T ExecuteCore(IDataCommand dataCommand, DbParameter[] para /// /// The data command to execute. /// The parameters to use in the command. - /// + /// A a new instance. + /// + /// cannot be null -or- + /// cannot be null. + /// protected override DbCommand GetCommandCore(IDataCommand dataCommand, params DbParameter[] parameters) { - if (dataCommand == null) { throw new ArgumentNullException(nameof(dataCommand)); } - if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } - SqlCommand command; - SqlCommand tempCommand = null; - try + Validator.ThrowIfNull(dataCommand, nameof(dataCommand)); + Validator.ThrowIfNull(parameters, nameof(parameters)); + return Patterns.SafeInvoke(() => new SqlCommand(dataCommand.Text, new SqlConnection(ConnectionString)), sc => + { + AddSqlParameters(sc, parameters); + return sc; + }, ex => throw ExceptionInsights.Embed(new InvalidOperationException("There is an error when creating a new SqlCommand.", ex), MethodBase.GetCurrentMethod(), Arguments.ToArray(dataCommand, parameters))); + } + + private static void AddSqlParameters(SqlCommand command, IEnumerable parameters) + { + foreach (var parameter in parameters) { - tempCommand = new SqlCommand(dataCommand.Text, new SqlConnection(ConnectionString)); - foreach (var parameter in parameters) + if (parameter is SqlParameter sqlParameter) { - if (parameter is SqlParameter sqlParameter) - { - // handle dates so they are compatible with SQL 200X and forward - if (sqlParameter.SqlDbType == SqlDbType.SmallDateTime || sqlParameter.SqlDbType == SqlDbType.DateTime) - { - if (parameter.Value != null && DateTime.TryParse(parameter.Value.ToString(), out var dateTime)) - { - if (dateTime == DateTime.MinValue) - { - parameter.Value = sqlParameter.SqlDbType == SqlDbType.DateTime ? DateTime.Parse("1753-01-01", CultureInfo.InvariantCulture) : DateTime.Parse("1900-01-01", CultureInfo.InvariantCulture); - } - - if (dateTime == DateTime.MaxValue && sqlParameter.SqlDbType == SqlDbType.SmallDateTime) - { - parameter.Value = DateTime.Parse("2079-06-01", CultureInfo.InvariantCulture); - } - } - } - } - - if (parameter.Value == null) { parameter.Value = DBNull.Value; } - tempCommand.Parameters.Add(parameter); + // handle dates so they are compatible with SQL 200X and forward + if (sqlParameter.SqlDbType == SqlDbType.SmallDateTime || sqlParameter.SqlDbType == SqlDbType.DateTime) { HandleSqlDateTime(sqlParameter); } } - command = tempCommand; - tempCommand = null; + + if (parameter.Value == null) { parameter.Value = DBNull.Value; } + command.Parameters.Add(parameter); } - finally + } + + private static void HandleSqlDateTime(SqlParameter parameter) + { + if (parameter.Value != null && DateTime.TryParse(parameter.Value.ToString(), out var dateTime)) { - if (tempCommand != null) { tempCommand.Dispose(); } + if (dateTime == DateTime.MinValue) + { + parameter.Value = parameter.SqlDbType == SqlDbType.DateTime ? DateTime.Parse("1753-01-01", CultureInfo.InvariantCulture) : DateTime.Parse("1900-01-01", CultureInfo.InvariantCulture); + } + + if (dateTime == DateTime.MaxValue && parameter.SqlDbType == SqlDbType.SmallDateTime) + { + parameter.Value = DateTime.Parse("2079-06-01", CultureInfo.InvariantCulture); + } } - return command; } private static SqlException ParseException(Exception exception) diff --git a/src/Cuemon.Data/DataManager.cs b/src/Cuemon.Data/DataManager.cs index 172e40648..7e83926a1 100644 --- a/src/Cuemon.Data/DataManager.cs +++ b/src/Cuemon.Data/DataManager.cs @@ -585,7 +585,7 @@ private void OpenConnection(DbCommand command) /// /// The data command to execute. /// The parameters to use in the command. - /// + /// An instance of a implementation. protected abstract DbCommand GetCommandCore(IDataCommand dataCommand, params DbParameter[] parameters); #endregion } From 78e4295aa6c8c3045e9ed63f02a7b5ecc2d9a48a Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 7 Oct 2020 04:18:46 +0200 Subject: [PATCH 273/385] Extended with ExcludePublic. --- src/Cuemon.Core/Reflection/MemberReflection.cs | 5 ++++- src/Cuemon.Core/Reflection/MemberReflectionOptions.cs | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Cuemon.Core/Reflection/MemberReflection.cs b/src/Cuemon.Core/Reflection/MemberReflection.cs index 8419820a6..50cbaf754 100644 --- a/src/Cuemon.Core/Reflection/MemberReflection.cs +++ b/src/Cuemon.Core/Reflection/MemberReflection.cs @@ -29,12 +29,14 @@ public static implicit operator BindingFlags(MemberReflection mr) /// if set to true non-public members are excluded from the binding constraint. /// if set to true static members are excluded from the binding constraint. /// if set to true derived members of a type's inheritance path are excluded from the binding constraint. - public MemberReflection(bool excludePrivate = false, bool excludeStatic = false, bool excludeInheritancePath = false) : + /// if set to true public members are excluded from the binding constraint. + public MemberReflection(bool excludePrivate = false, bool excludeStatic = false, bool excludeInheritancePath = false, bool excludePublic = false) : this(o => { o.ExcludeInheritancePath = excludeInheritancePath; o.ExcludePrivate = excludePrivate; o.ExcludeStatic = excludeStatic; + o.ExcludePublic = excludePublic; }) { } @@ -50,6 +52,7 @@ public MemberReflection(Action setup) if (options.ExcludePrivate) { flags &= ~BindingFlags.NonPublic; } if (options.ExcludeStatic) { flags &= ~BindingFlags.Static; } if (options.ExcludeInheritancePath) { flags |= BindingFlags.DeclaredOnly; } + if (options.ExcludePublic) { flags &= ~BindingFlags.Public; } Flags = flags; } diff --git a/src/Cuemon.Core/Reflection/MemberReflectionOptions.cs b/src/Cuemon.Core/Reflection/MemberReflectionOptions.cs index d77a777aa..ece279157 100644 --- a/src/Cuemon.Core/Reflection/MemberReflectionOptions.cs +++ b/src/Cuemon.Core/Reflection/MemberReflectionOptions.cs @@ -27,6 +27,10 @@ public class MemberReflectionOptions /// /// false /// + /// + /// + /// false + /// /// /// public MemberReflectionOptions() @@ -34,6 +38,7 @@ public MemberReflectionOptions() ExcludePrivate = false; ExcludeStatic = false; ExcludeInheritancePath = false; + ExcludePublic = false; } /// @@ -53,5 +58,11 @@ public MemberReflectionOptions() /// /// true if derived members of a type's inheritance path are excluded from the binding constraint; otherwise, false. public bool ExcludeInheritancePath { get; set; } + + /// + /// Gets or sets a value indicating whether public members are excluded from the binding constraint. + /// + /// true if public members are excluded from the binding constraint; otherwise, false. + public bool ExcludePublic { get; set; } } } \ No newline at end of file From 393a70c81226e8861b6dd6ba2bec164bc9063752 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 7 Oct 2020 04:19:13 +0200 Subject: [PATCH 274/385] Merged if statement. --- src/Cuemon.Data.SqlClient/SqlDataManager.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Cuemon.Data.SqlClient/SqlDataManager.cs b/src/Cuemon.Data.SqlClient/SqlDataManager.cs index e820327ed..44865a74b 100644 --- a/src/Cuemon.Data.SqlClient/SqlDataManager.cs +++ b/src/Cuemon.Data.SqlClient/SqlDataManager.cs @@ -206,10 +206,9 @@ private static void AddSqlParameters(SqlCommand command, IEnumerable Date: Wed, 7 Oct 2020 04:21:41 +0200 Subject: [PATCH 275/385] Opt-in for convention based assigment of properties on HostTest. Reason is, that we need Configuration and HostingEnvironment JIT for services to be configured. The lesser of two evils in time of coding. --- .../GlobalSuppressions.cs | 9 ++++ .../HostFixture.cs | 47 ++++++++----------- .../HostFixtureExtensions.cs | 2 +- .../HostTest.cs | 18 ++++--- .../IHostFixture.cs | 27 ++--------- 5 files changed, 43 insertions(+), 60 deletions(-) create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/GlobalSuppressions.cs diff --git a/src/Cuemon.Extensions.Xunit.Hosting/GlobalSuppressions.cs b/src/Cuemon.Extensions.Xunit.Hosting/GlobalSuppressions.cs new file mode 100644 index 000000000..1dd1d65e6 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/GlobalSuppressions.cs @@ -0,0 +1,9 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Minor Code Smell", "S3459:Unassigned members should be removed", Justification = "False-Positive. Convention based assignment to variable.", Scope = "member", Target = "~F:Cuemon.Extensions.Xunit.Hosting.HostTest`1._configuration")] +[assembly: SuppressMessage("Minor Code Smell", "S3459:Unassigned members should be removed", Justification = "False-Positive. Convention based assignment to variable.", Scope = "member", Target = "~F:Cuemon.Extensions.Xunit.Hosting.HostTest`1._hostingEnvironment")] diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs index acff32475..e60f6ea07 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Reflection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -23,10 +24,20 @@ public HostFixture() /// /// Creates and configures the of this instance. /// - /// The type of the object that inherits from . - /// was added to support those cases where the caller is required in the host configuration. - public virtual void ConfigureHost(Type hostTestType) + /// The object that inherits from . + /// was added to support those cases where the caller is required in the host configuration. + /// + /// is null. + /// + /// + /// is not assignable from . + /// + public virtual void ConfigureHost(Test hostTest) { + var hostTestType = hostTest?.GetType(); + Validator.ThrowIfNull(hostTest, nameof(hostTest)); + Validator.ThrowIfNotContainsType(hostTestType, nameof(hostTestType), $"{nameof(hostTest)} is not assignable from HostTest.", typeof(HostTest<>)); + Host = new HostBuilder() .ConfigureHostConfiguration(config => config.AddEnvironmentVariables("DOTNET_")) .ConfigureAppConfiguration((context, config) => @@ -38,9 +49,9 @@ public virtual void ConfigureHost(Type hostTestType) }) .ConfigureServices((context, services) => { + hostTestType.GetField("_configuration", BindingFlags.NonPublic).SetValue(hostTest, context.Configuration); + hostTestType.GetField("_hostingEnvironment", BindingFlags.NonPublic).SetValue(hostTest, context.HostingEnvironment); ConfigureServicesCallback(services); - Configuration = context.Configuration; - HostingEnvironment = context.HostingEnvironment; ServiceProvider = services.BuildServiceProvider(); }).Build(); } @@ -52,36 +63,16 @@ public virtual void ConfigureHost(Type hostTestType) public Action ConfigureServicesCallback { get; set; } /// - /// Gets the initialized by this instance. + /// Gets or sets the initialized by this instance. /// /// The initialized by this instance. - public IHost Host { get; private set; } + public IHost Host { get; protected set; } /// /// Gets the initialized by this instance. /// /// The initialized by this instance. - public IServiceProvider ServiceProvider { get; private set; } - - /// - /// Gets the initialized by this instance. - /// - /// The initialized by this instance. - public IConfiguration Configuration { get; private set; } - - #if NETSTANDARD - /// - /// Gets the initialized by this instance. - /// - /// The initialized by this instance. - public IHostingEnvironment HostingEnvironment { get; private set; } - #elif NETCOREAPP - /// - /// Gets the initialized by this instance. - /// - /// The initialized by this instance. - public IHostEnvironment HostingEnvironment { get; private set; } - #endif + public IServiceProvider ServiceProvider { get; protected set; } /// /// Called when this object is being disposed by either or having disposing set to true and is false. diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostFixtureExtensions.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostFixtureExtensions.cs index e720117a4..779f431a9 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostFixtureExtensions.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostFixtureExtensions.cs @@ -4,7 +4,7 @@ internal static class HostFixtureExtensions { internal static bool HasValidState(this IHostFixture fixture) { - return fixture.ConfigureServicesCallback != null && fixture.Host != null && fixture.ServiceProvider != null && fixture.Configuration != null; + return fixture.ConfigureServicesCallback != null && fixture.Host != null && fixture.ServiceProvider != null; } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs index 6c303e8be..d398b9ffe 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs @@ -17,6 +17,13 @@ namespace Cuemon.Extensions.Xunit.Hosting /// The class needed to be designed in this rather complex way, as this is the only way that xUnit supports a shared context. The need for shared context is theoretical at best, but it does opt-in for Scoped instances. public abstract class HostTest : Test, IClassFixture where T : class, IHostFixture { + private readonly IConfiguration _configuration; + #if NETSTANDARD + private readonly IHostingEnvironment _hostingEnvironment; + #elif NETCOREAPP + private readonly IHostEnvironment _hostingEnvironment; + #endif + /// /// Initializes a new instance of the class. /// @@ -28,13 +35,10 @@ protected HostTest(IHostFixture hostFixture, ITestOutputHelper output = null) : if (!hostFixture.HasValidState()) { hostFixture.ConfigureServicesCallback = ConfigureServices; - hostFixture.ConfigureHost(GetType()); + hostFixture.ConfigureHost(this); } - Host = hostFixture.Host; ServiceProvider = hostFixture.ServiceProvider; - Configuration = hostFixture.Configuration; - HostingEnvironment = hostFixture.HostingEnvironment; } /// @@ -53,20 +57,20 @@ protected HostTest(IHostFixture hostFixture, ITestOutputHelper output = null) : /// Gets the initialized by the . /// /// The initialized by the . - public IConfiguration Configuration { get; } + public IConfiguration Configuration => _configuration; #if NETSTANDARD /// /// Gets the initialized by the . /// /// The initialized by the . - public IHostingEnvironment HostingEnvironment { get; } + public IHostingEnvironment HostingEnvironment => _hostingEnvironment; #elif NETCOREAPP /// /// Gets the initialized by the . /// /// The initialized by the . - public IHostEnvironment HostingEnvironment { get; } + public IHostEnvironment HostingEnvironment => _hostingEnvironment; #endif /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs index 54dc8a8a5..7d54bdfc2 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs @@ -1,5 +1,4 @@ using System; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -29,31 +28,11 @@ public interface IHostFixture : IDisposable /// The initialized by the . IServiceProvider ServiceProvider { get; } - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - IConfiguration Configuration { get; } - - #if NETSTANDARD - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - IHostingEnvironment HostingEnvironment { get; } - #elif NETCOREAPP - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - IHostEnvironment HostingEnvironment { get; } - #endif - /// /// Creates and configures the of this . /// - /// The type of the object that inherits from . - /// was added to support those cases where the caller is required in the host configuration. - void ConfigureHost(Type hostTestType); + /// The object that inherits from . + /// was added to support those cases where the caller is required in the host configuration. + void ConfigureHost(Test hostTest); } } \ No newline at end of file From 1ceed55db9b554cfd51af6ac9ec229f9891a1748 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 8 Oct 2020 01:41:21 +0200 Subject: [PATCH 276/385] Opt-in for convention based assigment of properties on HostTest. Reason is, that we need Configuration and HostingEnvironment JIT for services to be configured. The lesser of two evils in time of coding. --- src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs index e60f6ea07..81ab3d339 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs @@ -1,6 +1,8 @@ using System; using System.IO; +using System.Linq; using System.Reflection; +using Cuemon.Reflection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -49,8 +51,10 @@ public virtual void ConfigureHost(Test hostTest) }) .ConfigureServices((context, services) => { - hostTestType.GetField("_configuration", BindingFlags.NonPublic).SetValue(hostTest, context.Configuration); - hostTestType.GetField("_hostingEnvironment", BindingFlags.NonPublic).SetValue(hostTest, context.HostingEnvironment); + var flags = new MemberReflection(excludeStatic: true, excludePublic: true).Flags; + var hostTestTypeBase = Decorator.Enclose(hostTestType).GetInheritedTypes().Single(t => t.BaseType == typeof(Test)); + hostTestTypeBase.GetField("_configuration", flags).SetValue(hostTest, context.Configuration); + hostTestTypeBase.GetField("_hostingEnvironment", flags).SetValue(hostTest, context.HostingEnvironment); ConfigureServicesCallback(services); ServiceProvider = services.BuildServiceProvider(); }).Build(); From 60803df29a17e18919082b5725722685e6130cef Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 8 Oct 2020 01:42:03 +0200 Subject: [PATCH 277/385] First unit/integration test with SQL server dependency. --- .../Assets/UserSecretsHostFixture.cs | 41 ++++++ .../Cuemon.Data.SqlClient.Tests.csproj | 20 +++ .../SqlDataManagerTest.cs | 126 ++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs create mode 100644 test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj create mode 100644 test/Cuemon.Data.SqlClient.Tests/SqlDataManagerTest.cs diff --git a/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs b/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs new file mode 100644 index 000000000..4fedb3503 --- /dev/null +++ b/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs @@ -0,0 +1,41 @@ +using System.IO; +using System.Linq; +using Cuemon.Extensions.Xunit; +using Cuemon.Extensions.Xunit.Hosting; +using Cuemon.Reflection; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Data.SqlClient.Assets +{ + public sealed class UserSecretsHostFixture : HostFixture + { + public override void ConfigureHost(Test hostTest) + { + var hostTestType = hostTest?.GetType(); + Validator.ThrowIfNull(hostTest, nameof(hostTest)); + Validator.ThrowIfNotContainsType(hostTestType, nameof(hostTestType), $"{nameof(hostTest)} is not assignable from HostTest.", typeof(HostTest<>)); + + Host = new HostBuilder() + .ConfigureHostConfiguration(config => config.AddEnvironmentVariables("DOTNET_")) + .ConfigureAppConfiguration((context, config) => + { + config.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) + .AddEnvironmentVariables() + .AddUserSecrets(); + }) + .ConfigureServices((context, services) => + { + var flags = new MemberReflection(excludeStatic: true, excludePublic: true).Flags; + var hostTestTypeBase = Decorator.Enclose(hostTestType).GetInheritedTypes().Single(t => t.BaseType == typeof(Test)); + hostTestTypeBase.GetField("_configuration", flags).SetValue(hostTest, context.Configuration); + hostTestTypeBase.GetField("_hostingEnvironment", flags).SetValue(hostTest, context.HostingEnvironment); + ConfigureServicesCallback(services); + ServiceProvider = services.BuildServiceProvider(); + }).Build(); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj b/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj new file mode 100644 index 000000000..66f841458 --- /dev/null +++ b/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj @@ -0,0 +1,20 @@ + + + + Cuemon.Data.SqlClient + a3ad04eb-1ef8-4aa4-a20d-cc87b2467342 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Cuemon.Data.SqlClient.Tests/SqlDataManagerTest.cs b/test/Cuemon.Data.SqlClient.Tests/SqlDataManagerTest.cs new file mode 100644 index 000000000..563ae9613 --- /dev/null +++ b/test/Cuemon.Data.SqlClient.Tests/SqlDataManagerTest.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Linq; +using Cuemon.Data.SqlClient.Assets; +using Cuemon.Extensions; +using Cuemon.Extensions.Data; +using Cuemon.Extensions.Xunit.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Data.SqlClient +{ + public class SqlDataManagerTest : HostTest + { + private readonly SqlDataManager _manager; + + public SqlDataManagerTest(UserSecretsHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _manager = hostFixture.ServiceProvider.GetRequiredService(); + } + + [Fact] + public void ExecuteReader_ShouldReadAllProducts() + { + using (var reader = _manager.ExecuteReader(new DataCommand("SELECT * FROM [Production].[Product]"))) + { + var rows = reader.ToRows(); + var columns = rows.ColumnNames.ToList(); + Assert.Equal(504, rows.Count); + Assert.Equal(25, columns.Count); + Assert.InRange(rows.Select(row => row["ModifiedDate"].As().Year).Distinct().Single(), 2013, 2015); + } + } + + [Fact] + public void ExecuteScalarAsInt32_ShouldInsertNewRow() + { + var existsBefore = _manager.ExecuteExists(new DataCommand("SELECT * FROM [ErrorLog]")); + var current = _manager.ExecuteScalarAsInt32(new DataCommand("SELECT COUNT(*) FROM [ErrorLog]")); + var affected = _manager.Execute(new DataCommand(@"INSERT INTO [ErrorLog] ([ErrorTime] +,[UserName] +,[ErrorNumber] +,[ErrorSeverity] +,[ErrorState] +,[ErrorProcedure] +,[ErrorLine] +,[ErrorMessage]) +VALUES +(@utcNow, +@userName, +@errNo, +@errSeverity, +@errState, +@errProcedure, +@errorLine, +@errorMessage +)"), + new SqlParameter("@utcNow", DateTime.UtcNow), + new SqlParameter("@userName", "MMORT"), + new SqlParameter("@errNo", 42), + new SqlParameter("@errSeverity", 1), + new SqlParameter("@errState", 5), + new SqlParameter("@errProcedure", "Do not try this at home."), + new SqlParameter("@errorLine", 215), + new SqlParameter("@errorMessage", "Catastrophic failure.")); + var after = _manager.ExecuteScalarAsInt32(new DataCommand("SELECT COUNT(*) FROM [ErrorLog]")); + var existsAfter = _manager.ExecuteExists(new DataCommand("SELECT * FROM [ErrorLog]")); + + Assert.False(existsBefore); + Assert.Equal(0, current); + Assert.Equal(1, affected); + Assert.Equal(1, after); + Assert.True(existsAfter); + } + + + [Fact] + public void Execute_ShouldUpdateRows() + { + DataTransferRowCollection before; + using (var reader = _manager.ExecuteReader(new DataCommand("SELECT * FROM [HumanResources].[Department]"))) + { + before = reader.ToRows(); + } + + var affected = _manager.Execute(new DataCommand("UPDATE [HumanResources].[Department] SET [Name] = [Name] + ' XXX'")); + + DataTransferRowCollection after; + using (var reader = _manager.ExecuteReader(new DataCommand("SELECT * FROM [HumanResources].[Department]"))) + { + after = reader.ToRows(); + } + + Assert.Equal(16, before.Count); + Assert.Equal(16, affected); + Assert.Equal(16, after.Count); + + for (var i = 0; i < before.Count; i++) + { + Assert.StartsWith(before[i]["Name"].As(), after[i]["Name"].As()); + Assert.NotEqual(before[i]["Name"].As(), after[i]["Name"].As()); + Assert.EndsWith("XXX", after[i]["Name"].As()); + } + } + + [Fact] + public void Execute_ShouldDeleteRows() + { + var before = _manager.ExecuteScalarAsInt32(new DataCommand("SELECT COUNT(*) FROM [HumanResources].[EmployeeDepartmentHistory]")); + var affected = _manager.Execute(new DataCommand("DELETE [HumanResources].[EmployeeDepartmentHistory] WHERE BusinessEntityID >= 270")); + var after = _manager.ExecuteScalarAsInt32(new DataCommand("SELECT COUNT(*) FROM [HumanResources].[EmployeeDepartmentHistory]")); + Assert.Equal(296, before); + Assert.Equal(21, affected); + Assert.Equal(275, after); + } + + public override void ConfigureServices(IServiceCollection services) + { + var cnn = Configuration.GetConnectionString("AdventureWorks"); + services.AddSingleton(new SqlDataManager(cnn)); + } + } +} \ No newline at end of file From f84b2a48de3977c7c0f7447f4ea75da2851c8863 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 8 Oct 2020 01:48:19 +0200 Subject: [PATCH 278/385] Updated package description, release notes and DocFx namespace description. --- docfx/api/namespaces/Cuemon.Data.SqlClient.md | 15 +++++- .../Cuemon.Data.SqlClient.csproj | 5 +- .../Properties/PackageReleaseNotes.txt | 50 +++++++++++++++++++ 3 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 src/Cuemon.Data/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Data.SqlClient.md b/docfx/api/namespaces/Cuemon.Data.SqlClient.md index dd2e8631a..9435bf3d8 100644 --- a/docfx/api/namespaces/Cuemon.Data.SqlClient.md +++ b/docfx/api/namespaces/Cuemon.Data.SqlClient.md @@ -2,4 +2,17 @@ uid: Cuemon.Data.SqlClient summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Data.SqlClient namespace contains types that provide ways for developers to work with Microsoft SQL Server integrations. The namespace is an addition to the System.Data.SqlClient namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Data.SqlClient namespace](https://docs.microsoft.com/en-us/dotnet/api/system.Data.SqlClient?view=netframework-4.8) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Data.SqlClient)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Data.SqlClient)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Data.SqlClient) + +NuGet packages 📦\ +[Cuemon.Data.SqlClient (CI)](https://nuget.cuemon.net/packages/Cuemon.Data.SqlClient)\ +[Cuemon.Data.SqlClient (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Data.SqlClient) \ No newline at end of file diff --git a/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj b/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj index 221b9aa90..b05f5cd52 100644 --- a/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj +++ b/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj @@ -8,8 +8,7 @@ Cuemon.Data.SqlClient Cuemon.Data.SqlClient - The Cuemon.Data.SqlClient namespace contains types that provide ways for developers to work with Microsoft SQL Server integrations. Also includes a flexible, generic and lambda friendly way to perform both synchronous and asynchronous time measuring operations. The namespace is an addition to the System.Diagnostics namespace. - The Cuemon.Data.SqlClient namespace contains different Microsoft SQL Server implementations of different abstractions found in the Cuemon.Data namespace. + The Cuemon.Data.SqlClient namespace contains types that provide ways for developers to work with Microsoft SQL Server integrations. The namespace is an addition to the System.Data.SqlClient namespace. sql sql-in-operator sql-query-builder sql-data-manager transient-fault-handling @@ -19,7 +18,7 @@ - + \ No newline at end of file diff --git a/src/Cuemon.Data/Properties/PackageReleaseNotes.txt b/src/Cuemon.Data/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..83c78cee7 --- /dev/null +++ b/src/Cuemon.Data/Properties/PackageReleaseNotes.txt @@ -0,0 +1,50 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# Upgrade Steps +- To use the earlier built-in support for Microsoft SQL Server, please refer to the Cuemon.Data.SqlClient namespace, as it has been merged and refactored out of this assembly +  +# Breaking Changes +- REMOVED StringFormatter class from the Cuemon namespace +- REMOVED StandardizedDateTimeFormatPattern enum from the Cuemon namespace +- MOVED AsyncOptions class in the Cuemon.Threading namespace to its own assembly (by the same name and namespace) +- REMOVED JsonWebToken class from the Cuemon.Security.Web namespace +- REMOVED JsonWebTokenHashAlgorithm class from the Cuemon.Security.Web namespace +- REMOVED JsonWebTokenHashAlgorithmConverter class from the Cuemon.Security.Web namespace +- REMOVED JsonWebTokenHeader class from the Cuemon.Security.Web namespace +- REMOVED JsonWebTokenPayload class from the Cuemon.Security.Web namespace +- REMOVED Obfuscator class from the Cuemon.Security namespace +- REMOVED ObfuscatorMapping class from the Cuemon.Security namespace +- REMOVED SecurityToken class from the Cuemon.Security namespace +- REMOVED SecurityTokenSettings class from the Cuemon.Security namespace (replaced with SignedUriOptions in the Cuemon.Extensions.Net.Security namespace) +- REMOVED SecurityUtility class from the Cuemon.Security namespace +- REMOVED AssemblyExtensions class from the Cuemon.Reflection namespace +- MOVED MemberInfoExtensions class from the Cuemon.Reflection namespace to Cuemon.Extensions.Reflection namespace +- REMOVED MethodBaseConverterExtensions class from the Cuemon.Reflection namespace +- MOVED LatencyException class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- MOVED TransientOperation class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- MOVED TransientFaultEvidence class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- MOVED TransientFaultException class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- MOVED TransientOperation class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- MOVED TransientOperationOptions class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) +- REMOVED IMessageLocalizer interface from the Cuemon.Globalization namespace +  +# New Features +- ADDED ResourceAttribute class in the Cuemon.Globalization namespace that provides a generic way to support localization on attribute decorated methods +- +  +# Bug Fixes +- +- +  +# Improvements +- +- +  +# Quality Actions +- +- +  +# Other Changes +- +- \ No newline at end of file From 60831cce10721076e593c992912cdf90b9b9c0b8 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 8 Oct 2020 01:48:43 +0200 Subject: [PATCH 279/385] Added Cuemon.Data.SqlClient.Tests. --- Cuemon.sln | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Cuemon.sln b/Cuemon.sln index 975d9193b..f829e576a 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -123,7 +123,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Runtime.C EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Runtime.Caching.Tests", "test\Cuemon.Extensions.Runtime.Caching.Tests\Cuemon.Extensions.Runtime.Caching.Tests.csproj", "{0F614FD1-BC7C-4F7F-9847-D3675614576C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Newtonsoft.Json.Tests", "test\Cuemon.Extensions.Newtonsoft.Json.Tests\Cuemon.Extensions.Newtonsoft.Json.Tests.csproj", "{8A3E26BD-A3C4-4684-909B-1ABDFDB4108D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Newtonsoft.Json.Tests", "test\Cuemon.Extensions.Newtonsoft.Json.Tests\Cuemon.Extensions.Newtonsoft.Json.Tests.csproj", "{8A3E26BD-A3C4-4684-909B-1ABDFDB4108D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Data.SqlClient.Tests", "test\Cuemon.Data.SqlClient.Tests\Cuemon.Data.SqlClient.Tests.csproj", "{A9610C9E-1944-4771-A5E1-CE47ADA243D5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -367,6 +369,10 @@ Global {8A3E26BD-A3C4-4684-909B-1ABDFDB4108D}.Debug|Any CPU.Build.0 = Debug|Any CPU {8A3E26BD-A3C4-4684-909B-1ABDFDB4108D}.Release|Any CPU.ActiveCfg = Release|Any CPU {8A3E26BD-A3C4-4684-909B-1ABDFDB4108D}.Release|Any CPU.Build.0 = Release|Any CPU + {A9610C9E-1944-4771-A5E1-CE47ADA243D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9610C9E-1944-4771-A5E1-CE47ADA243D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9610C9E-1944-4771-A5E1-CE47ADA243D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9610C9E-1944-4771-A5E1-CE47ADA243D5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -431,6 +437,7 @@ Global {1F0BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} {0F614FD1-BC7C-4F7F-9847-D3675614576C} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {8A3E26BD-A3C4-4684-909B-1ABDFDB4108D} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} + {A9610C9E-1944-4771-A5E1-CE47ADA243D5} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2A848386-B682-4F6D-8254-B5F6247C3054} From c12178629f937cbcc90c544da338c65b1cb0b854 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 8 Oct 2020 01:49:36 +0200 Subject: [PATCH 280/385] Added support for unit/integration test using SQL Server, AdventureWorks and Docker. --- azure-pipelines.yml | 37 ++++++++++++++++++++++++++++++++----- docker-compose.yml | 14 ++++++++++++++ 2 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 docker-compose.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ed603b7c4..95449a452 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -2,11 +2,17 @@ trigger: - development variables: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - BuildSource: 'src' - BuildPlatform: 'Any CPU' - BuildConfiguration: 'Release' + - group: Integration Test + - name: DOTNET_SKIP_FIRST_TIME_EXPERIENCE + value: true + - name: DOTNET_CLI_TELEMETRY_OPTOUT + value: 1 + - name: BuildSource + value: 'src' + - name: BuildPlatform + value: 'Any CPU' + - name: BuildConfiguration + value: 'Release' jobs: - job: CI @@ -23,6 +29,7 @@ jobs: vmImage: $(imageName) steps: + - task: UseDotNet@2 condition: eq(variables['Agent.OS'], 'Linux') displayName: 'Use .Net Core SDK 2.2.207 (SonarCloud)' @@ -71,6 +78,17 @@ jobs: projects: | **/*.csproj + - task: DockerCompose@0 + condition: eq(variables['Agent.OS'], 'Linux') + displayName: 'Spin up SQL Server for unit/integration test' + inputs: + containerregistrytype: 'Container Registry' + dockerComposeFile: '**/docker-compose.yml' + dockerComposeFileArgs: | + SA_PASSWORD=$(awsql-password) + dockerComposeCommand: "up -d" + action: 'Run a Docker Compose command' + - task: SonarCloudPrepare@1 condition: eq(variables['Agent.OS'], 'Linux') displayName: 'Prepare Analysis on SonarCloud' @@ -149,6 +167,15 @@ jobs: inputs: pollingTimeoutSec: '300' + - task: DockerCompose@0 + condition: eq(variables['Agent.OS'], 'Linux') + displayName: 'Spin down SQL Server' + inputs: + containerregistrytype: 'Container Registry' + dockerComposeFile: '**/docker-compose.yml' + dockerComposeCommand: "down" + action: 'Run a Docker Compose command' + - task: DotNetCoreCLI@2 condition: eq(variables['Agent.OS'], 'Windows_NT') displayName: dotnet pack diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..848892a50 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +version: "3.8" + +services: + database: + image: gimlichael/aw-mssql-server:2019-GA-ubuntu-16.04 + container_name: "awsql" + hostname: "awsql" + ports: + - "1433:1433" + environment: + SA_PASSWORD: + ACCEPT_EULA: "Y" + SECONDS_TO_AWAIT_SQLSERVER: "45" + MSSQL_PID: "Express" \ No newline at end of file From cde5c076f74b90bd5112a346ec31d834b44bec07 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 8 Oct 2020 02:03:11 +0200 Subject: [PATCH 281/385] Fixed bug that was introduced after change to convention based assigment of properties. _configuration and _hostingEnvironment is now set again after first init. --- .../HostFixture.cs | 26 +++++++++++++++++-- .../HostTest.cs | 2 ++ .../IHostFixture.cs | 21 +++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs index 81ab3d339..b62e9f2b0 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs @@ -55,6 +55,8 @@ public virtual void ConfigureHost(Test hostTest) var hostTestTypeBase = Decorator.Enclose(hostTestType).GetInheritedTypes().Single(t => t.BaseType == typeof(Test)); hostTestTypeBase.GetField("_configuration", flags).SetValue(hostTest, context.Configuration); hostTestTypeBase.GetField("_hostingEnvironment", flags).SetValue(hostTest, context.HostingEnvironment); + Configuration = context.Configuration; + HostingEnvironment = context.HostingEnvironment; ConfigureServicesCallback(services); ServiceProvider = services.BuildServiceProvider(); }).Build(); @@ -70,13 +72,33 @@ public virtual void ConfigureHost(Test hostTest) /// Gets or sets the initialized by this instance. /// /// The initialized by this instance. - public IHost Host { get; protected set; } + public IHost Host { get; private set; } /// /// Gets the initialized by this instance. /// /// The initialized by this instance. - public IServiceProvider ServiceProvider { get; protected set; } + public IServiceProvider ServiceProvider { get; private set; } + + /// + /// Gets the initialized by this instance. + /// + /// The initialized by this instance. + public IConfiguration Configuration { get; private set; } + + #if NETSTANDARD + /// + /// Gets the initialized by this instance. + /// + /// The initialized by this instance. + public IHostingEnvironment HostingEnvironment { get; private set; } + #elif NETCOREAPP + /// + /// Gets the initialized by this instance. + /// + /// The initialized by this instance. + public IHostEnvironment HostingEnvironment { get; private set; } + #endif /// /// Called when this object is being disposed by either or having disposing set to true and is false. diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs index d398b9ffe..409cb8fb9 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs @@ -37,6 +37,8 @@ protected HostTest(IHostFixture hostFixture, ITestOutputHelper output = null) : hostFixture.ConfigureServicesCallback = ConfigureServices; hostFixture.ConfigureHost(this); } + if (_configuration == null) { _configuration = hostFixture.Configuration; } + if (_hostingEnvironment == null) { _hostingEnvironment = hostFixture.HostingEnvironment; } Host = hostFixture.Host; ServiceProvider = hostFixture.ServiceProvider; } diff --git a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs index 7d54bdfc2..8df22df7a 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -28,6 +29,26 @@ public interface IHostFixture : IDisposable /// The initialized by the . IServiceProvider ServiceProvider { get; } + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IConfiguration Configuration { get; } + + #if NETSTANDARD + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IHostingEnvironment HostingEnvironment { get; } + #elif NETCOREAPP + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IHostEnvironment HostingEnvironment { get; } + #endif + /// /// Creates and configures the of this . /// From 19dc1376205af2440452a85472da5d10820af66a Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 8 Oct 2020 02:05:22 +0200 Subject: [PATCH 282/385] Mistakenly set protected to private; reverted. --- src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs index b62e9f2b0..5b87c7a24 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs @@ -72,13 +72,13 @@ public virtual void ConfigureHost(Test hostTest) /// Gets or sets the initialized by this instance. /// /// The initialized by this instance. - public IHost Host { get; private set; } + public IHost Host { get; protected set; } /// /// Gets the initialized by this instance. /// /// The initialized by this instance. - public IServiceProvider ServiceProvider { get; private set; } + public IServiceProvider ServiceProvider { get; protected set; } /// /// Gets the initialized by this instance. From 7b1c0dac4359db060eead87cd2d8e2ad952256fa Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 8 Oct 2020 09:59:00 +0200 Subject: [PATCH 283/385] Update azure-pipelines.yml Divided unit test based on Agent.OS. Added env. variable to unit test on Linux. Excluded SqlClient for unit test on Windows. --- azure-pipelines.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 95449a452..315d2bc4e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -134,12 +134,24 @@ jobs: workingDirectory: '$(BuildSource)' - task: DotNetCoreCLI@2 - displayName: 'Test Solution' + displayName: 'Test Solution - Linux' + condition: eq(variables['Agent.OS'], 'Linux') inputs: command: 'test' projects: test/**/*.csproj arguments: '--configuration $(BuildConfiguration) --collect:"XPlat Code Coverage" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover' publishTestResults: true + env: + ConnectionStrings__AdventureWorks: $(ConnectionStrings--AdventureWorks) + + - task: DotNetCoreCLI@2 + displayName: 'Test Solution - Windows' + condition: eq(variables['Agent.OS'], 'Windows_NT') + inputs: + command: 'test' + projects: test/**/*.csproj + arguments: '--configuration $(BuildConfiguration) --collect:"XPlat Code Coverage" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover --filter FullyQualifiedName!~SqlClient' + publishTestResults: true - script: reportgenerator "-reports:**/*.opencover.xml" "-targetdir:$(Build.SourcesDirectory)/Coverage" "-reporttypes:Cobertura;HTMLInline;HTMLChart" condition: eq(variables['Agent.OS'], 'Linux') @@ -169,7 +181,7 @@ jobs: - task: DockerCompose@0 condition: eq(variables['Agent.OS'], 'Linux') - displayName: 'Spin down SQL Server' + displayName: 'Take down SQL Server' inputs: containerregistrytype: 'Container Registry' dockerComposeFile: '**/docker-compose.yml' @@ -203,4 +215,4 @@ jobs: command: 'push' packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg' nuGetFeedType: 'external' - publishFeedCredentials: 'Cuemon-Nuget' \ No newline at end of file + publishFeedCredentials: 'Cuemon-Nuget' From 36a027dd02aa2cd1befdd9ef8f51b67e9039d3cb Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 9 Oct 2020 00:52:00 +0200 Subject: [PATCH 284/385] Further unit testing and minor tweaks to code. --- src/Cuemon.Data.SqlClient/SqlDataManager.cs | 16 ++--- src/Cuemon.Data.SqlClient/SqlQueryBuilder.cs | 5 +- src/Cuemon.Data/DataManager.cs | 60 +++++++++---------- src/Cuemon.Data/InOperatorResult.cs | 10 ++++ src/Cuemon.Data/QueryBuilder.cs | 4 +- .../SqlInOperatorTest.cs | 41 +++++++++++++ .../SqlQueryBuilderTest.cs | 59 ++++++++++++++++++ 7 files changed, 153 insertions(+), 42 deletions(-) create mode 100644 test/Cuemon.Data.SqlClient.Tests/SqlInOperatorTest.cs create mode 100644 test/Cuemon.Data.SqlClient.Tests/SqlQueryBuilderTest.cs diff --git a/src/Cuemon.Data.SqlClient/SqlDataManager.cs b/src/Cuemon.Data.SqlClient/SqlDataManager.cs index 44865a74b..ee3517fb6 100644 --- a/src/Cuemon.Data.SqlClient/SqlDataManager.cs +++ b/src/Cuemon.Data.SqlClient/SqlDataManager.cs @@ -105,7 +105,7 @@ public SqlDataManager(string connectionString) /// The data command to execute. /// The parameters to use in the command. /// - public override int ExecuteIdentityInt32(IDataCommand dataCommand, params DbParameter[] parameters) + public override int ExecuteIdentityInt32(IDataCommand dataCommand, params IDbDataParameter[] parameters) { if (dataCommand == null) throw new ArgumentNullException(nameof(dataCommand)); if (dataCommand.Type != CommandType.Text) { throw new ArgumentException("This method only supports CommandType.Text specifications.", nameof(dataCommand)); } @@ -121,7 +121,7 @@ public override int ExecuteIdentityInt32(IDataCommand dataCommand, params DbPara /// The data command to execute. /// The parameters to use in the command. /// - public override long ExecuteIdentityInt64(IDataCommand dataCommand, params DbParameter[] parameters) + public override long ExecuteIdentityInt64(IDataCommand dataCommand, params IDbDataParameter[] parameters) { if (dataCommand == null) throw new ArgumentNullException(nameof(dataCommand)); if (dataCommand.Type != CommandType.Text) { throw new ArgumentException("This method only supports CommandType.Text specifications.", nameof(dataCommand)); } @@ -137,7 +137,7 @@ public override long ExecuteIdentityInt64(IDataCommand dataCommand, params DbPar /// The data command to execute. /// The parameters to use in the command. /// - public override decimal ExecuteIdentityDecimal(IDataCommand dataCommand, params DbParameter[] parameters) + public override decimal ExecuteIdentityDecimal(IDataCommand dataCommand, params IDbDataParameter[] parameters) { if (dataCommand == null) throw new ArgumentNullException(nameof(dataCommand)); if (dataCommand.Type != CommandType.Text) { throw new ArgumentException("This method only supports CommandType.Text specifications.", nameof(dataCommand)); } @@ -159,12 +159,12 @@ public override DataManager Clone() } /// - /// Core method for executing methods on the object resolved from the virtual method. + /// Core method for executing methods on the object resolved from the virtual method. /// /// The type to return. /// The data command to execute. /// The parameters to use in the command. - /// The function delegate that will invoke a method on the resolved from the virtual method. + /// The function delegate that will invoke a method on the resolved from the virtual method. /// A value of that is equal to the invoked method of the object. /// /// If is null, no SQL operation is wrapped inside a transient fault handling operation. @@ -174,7 +174,7 @@ public override DataManager Clone() /// In case of a transient failure the default implementation will use .
/// In any other case the originating exception is thrown. ///
- protected override T ExecuteCore(IDataCommand dataCommand, DbParameter[] parameters, Func commandInvoker) + protected override T ExecuteCore(IDataCommand dataCommand, IDbDataParameter[] parameters, Func commandInvoker) { return TransientFaultHandlingOptionsCallback == null ? base.ExecuteCore(dataCommand, parameters, commandInvoker) @@ -191,7 +191,7 @@ protected override T ExecuteCore(IDataCommand dataCommand, DbParameter[] para /// cannot be null -or- /// cannot be null. /// - protected override DbCommand GetCommandCore(IDataCommand dataCommand, params DbParameter[] parameters) + protected override DbCommand GetCommandCore(IDataCommand dataCommand, params IDbDataParameter[] parameters) { Validator.ThrowIfNull(dataCommand, nameof(dataCommand)); Validator.ThrowIfNull(parameters, nameof(parameters)); @@ -202,7 +202,7 @@ protected override DbCommand GetCommandCore(IDataCommand dataCommand, params DbP }, ex => throw ExceptionInsights.Embed(new InvalidOperationException("There is an error when creating a new SqlCommand.", ex), MethodBase.GetCurrentMethod(), Arguments.ToArray(dataCommand, parameters))); } - private static void AddSqlParameters(SqlCommand command, IEnumerable parameters) + private static void AddSqlParameters(SqlCommand command, IEnumerable parameters) { foreach (var parameter in parameters) { diff --git a/src/Cuemon.Data.SqlClient/SqlQueryBuilder.cs b/src/Cuemon.Data.SqlClient/SqlQueryBuilder.cs index 1c482999d..abeae6880 100644 --- a/src/Cuemon.Data.SqlClient/SqlQueryBuilder.cs +++ b/src/Cuemon.Data.SqlClient/SqlQueryBuilder.cs @@ -41,13 +41,14 @@ public SqlQueryBuilder(string tableName, IDictionary keyColumns, #region Methods /// - /// Create and returns the builded query from the specified . + /// Create and returns the query from the specified . /// /// Type of the query to create. /// The name of the table or view. Overrides the class wide tableName. - /// + /// The result of the builder as a T-SQL query. public override string GetQuery(QueryType queryType, string tableName) { + tableName = tableName ?? TableName; switch (queryType) { case QueryType.Exists: diff --git a/src/Cuemon.Data/DataManager.cs b/src/Cuemon.Data/DataManager.cs index 7e83926a1..4e9c5dce7 100644 --- a/src/Cuemon.Data/DataManager.cs +++ b/src/Cuemon.Data/DataManager.cs @@ -186,7 +186,7 @@ public static string ReaderToString(DbDataReader value) /// /// A value. /// - public int Execute(IDataCommand dataCommand, params DbParameter[] parameters) + public int Execute(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteCore(dataCommand, parameters, dbCommand => { @@ -209,7 +209,7 @@ public int Execute(IDataCommand dataCommand, params DbParameter[] parameters) /// /// A value. /// - public bool ExecuteExists(IDataCommand dataCommand, params DbParameter[] parameters) + public bool ExecuteExists(IDataCommand dataCommand, params IDbDataParameter[] parameters) { using (var reader = ExecuteReader(dataCommand, parameters)) { @@ -223,7 +223,7 @@ public bool ExecuteExists(IDataCommand dataCommand, params DbParameter[] paramet /// The data command to execute. /// The parameters to use in the command. /// - public abstract int ExecuteIdentityInt32(IDataCommand dataCommand, params DbParameter[] parameters); + public abstract int ExecuteIdentityInt32(IDataCommand dataCommand, params IDbDataParameter[] parameters); /// /// Executes the command statement and returns an identity value as long. @@ -231,7 +231,7 @@ public bool ExecuteExists(IDataCommand dataCommand, params DbParameter[] paramet /// The data command to execute. /// The parameters to use in the command. /// - public abstract long ExecuteIdentityInt64(IDataCommand dataCommand, params DbParameter[] parameters); + public abstract long ExecuteIdentityInt64(IDataCommand dataCommand, params IDbDataParameter[] parameters); /// /// Executes the command statement and returns an identity value as decimal. @@ -239,7 +239,7 @@ public bool ExecuteExists(IDataCommand dataCommand, params DbParameter[] paramet /// The data command to execute. /// The parameters to use in the command. /// - public abstract decimal ExecuteIdentityDecimal(IDataCommand dataCommand, params DbParameter[] parameters); + public abstract decimal ExecuteIdentityDecimal(IDataCommand dataCommand, params IDbDataParameter[] parameters); /// /// Executes the command statement and returns an object supporting the DbDataReader interface. @@ -249,7 +249,7 @@ public bool ExecuteExists(IDataCommand dataCommand, params DbParameter[] paramet /// /// An object supporting the interface. /// - public DbDataReader ExecuteReader(IDataCommand dataCommand, params DbParameter[] parameters) + public DbDataReader ExecuteReader(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteCore(dataCommand, parameters, dbCommand => dbCommand.ExecuteReader(CommandBehavior.CloseConnection)); } @@ -262,7 +262,7 @@ public DbDataReader ExecuteReader(IDataCommand dataCommand, params DbParameter[] /// /// An object. /// - public virtual string ExecuteXmlString(IDataCommand dataCommand, params DbParameter[] parameters) + public virtual string ExecuteXmlString(IDataCommand dataCommand, params IDbDataParameter[] parameters) { using (var reader = ExecuteReader(dataCommand, parameters)) { @@ -277,7 +277,7 @@ public virtual string ExecuteXmlString(IDataCommand dataCommand, params DbParame /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from . - public object ExecuteScalar(IDataCommand dataCommand, params DbParameter[] parameters) + public object ExecuteScalar(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteCore(dataCommand, parameters, dbCommand => { @@ -301,7 +301,7 @@ public object ExecuteScalar(IDataCommand dataCommand, params DbParameter[] param /// The parameters to use in the command. /// The first column of the first row in the result from as the specified . /// This method uses when casting the first column of the first row in the result from . - public object ExecuteScalarAsType(IDataCommand dataCommand, Type returnType, params DbParameter[] parameters) + public object ExecuteScalarAsType(IDataCommand dataCommand, Type returnType, params IDbDataParameter[] parameters) { return ExecuteScalarAsType(dataCommand, returnType, CultureInfo.InvariantCulture, parameters); } @@ -315,7 +315,7 @@ public object ExecuteScalarAsType(IDataCommand dataCommand, Type returnType, par /// An object that supplies culture-specific formatting information. /// The parameters to use in the command. /// The first column of the first row in the result from as the specified . - public object ExecuteScalarAsType(IDataCommand dataCommand, Type returnType, IFormatProvider provider, params DbParameter[] parameters) + public object ExecuteScalarAsType(IDataCommand dataCommand, Type returnType, IFormatProvider provider, params IDbDataParameter[] parameters) { return Decorator.Enclose(ExecuteScalar(dataCommand, parameters)).ChangeType(returnType, o => o.FormatProvider = provider); } @@ -329,7 +329,7 @@ public object ExecuteScalarAsType(IDataCommand dataCommand, Type returnType, IFo /// The parameters to use in the command. /// The first column of the first row in the result from as . /// This method uses when casting the first column of the first row in the result from . - public TResult ExecuteScalarAs(IDataCommand dataCommand, params DbParameter[] parameters) + public TResult ExecuteScalarAs(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return (TResult)ExecuteScalarAsType(dataCommand, typeof(TResult), parameters); } @@ -343,7 +343,7 @@ public TResult ExecuteScalarAs(IDataCommand dataCommand, params DbParam /// An object that supplies culture-specific formatting information. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public TResult ExecuteScalarAs(IDataCommand dataCommand, IFormatProvider provider, params DbParameter[] parameters) + public TResult ExecuteScalarAs(IDataCommand dataCommand, IFormatProvider provider, params IDbDataParameter[] parameters) { return (TResult)ExecuteScalarAsType(dataCommand, typeof(TResult), provider, parameters); } @@ -355,7 +355,7 @@ public TResult ExecuteScalarAs(IDataCommand dataCommand, IFormatProvide /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public bool ExecuteScalarAsBoolean(IDataCommand dataCommand, params DbParameter[] parameters) + public bool ExecuteScalarAsBoolean(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -368,7 +368,7 @@ public bool ExecuteScalarAsBoolean(IDataCommand dataCommand, params DbParameter[ /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public DateTime ExecuteScalarAsDateTime(IDataCommand dataCommand, params DbParameter[] parameters) + public DateTime ExecuteScalarAsDateTime(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -380,7 +380,7 @@ public DateTime ExecuteScalarAsDateTime(IDataCommand dataCommand, params DbParam /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public short ExecuteScalarAsInt16(IDataCommand dataCommand, params DbParameter[] parameters) + public short ExecuteScalarAsInt16(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -392,7 +392,7 @@ public short ExecuteScalarAsInt16(IDataCommand dataCommand, params DbParameter[] /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public int ExecuteScalarAsInt32(IDataCommand dataCommand, params DbParameter[] parameters) + public int ExecuteScalarAsInt32(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -404,7 +404,7 @@ public int ExecuteScalarAsInt32(IDataCommand dataCommand, params DbParameter[] p /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public long ExecuteScalarAsInt64(IDataCommand dataCommand, params DbParameter[] parameters) + public long ExecuteScalarAsInt64(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -416,7 +416,7 @@ public long ExecuteScalarAsInt64(IDataCommand dataCommand, params DbParameter[] /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public byte ExecuteScalarAsByte(IDataCommand dataCommand, params DbParameter[] parameters) + public byte ExecuteScalarAsByte(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -428,7 +428,7 @@ public byte ExecuteScalarAsByte(IDataCommand dataCommand, params DbParameter[] p /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public sbyte ExecuteScalarAsSByte(IDataCommand dataCommand, params DbParameter[] parameters) + public sbyte ExecuteScalarAsSByte(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -440,7 +440,7 @@ public sbyte ExecuteScalarAsSByte(IDataCommand dataCommand, params DbParameter[] /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public decimal ExecuteScalarAsDecimal(IDataCommand dataCommand, params DbParameter[] parameters) + public decimal ExecuteScalarAsDecimal(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -452,7 +452,7 @@ public decimal ExecuteScalarAsDecimal(IDataCommand dataCommand, params DbParamet /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public double ExecuteScalarAsDouble(IDataCommand dataCommand, params DbParameter[] parameters) + public double ExecuteScalarAsDouble(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -464,7 +464,7 @@ public double ExecuteScalarAsDouble(IDataCommand dataCommand, params DbParameter /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public ushort ExecuteScalarAsUInt16(IDataCommand dataCommand, params DbParameter[] parameters) + public ushort ExecuteScalarAsUInt16(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -476,7 +476,7 @@ public ushort ExecuteScalarAsUInt16(IDataCommand dataCommand, params DbParameter /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public uint ExecuteScalarAsUInt32(IDataCommand dataCommand, params DbParameter[] parameters) + public uint ExecuteScalarAsUInt32(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -488,7 +488,7 @@ public uint ExecuteScalarAsUInt32(IDataCommand dataCommand, params DbParameter[] /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public ulong ExecuteScalarAsUInt64(IDataCommand dataCommand, params DbParameter[] parameters) + public ulong ExecuteScalarAsUInt64(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -500,7 +500,7 @@ public ulong ExecuteScalarAsUInt64(IDataCommand dataCommand, params DbParameter[ /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public string ExecuteScalarAsString(IDataCommand dataCommand, params DbParameter[] parameters) + public string ExecuteScalarAsString(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -513,7 +513,7 @@ public string ExecuteScalarAsString(IDataCommand dataCommand, params DbParameter /// The data command to execute. /// The parameters to use in the command. /// The first column of the first row in the result from as . - public Guid ExecuteScalarAsGuid(IDataCommand dataCommand, params DbParameter[] parameters) + public Guid ExecuteScalarAsGuid(IDataCommand dataCommand, params IDbDataParameter[] parameters) { return ExecuteScalarAs(dataCommand, parameters); } @@ -526,12 +526,12 @@ public Guid ExecuteScalarAsGuid(IDataCommand dataCommand, params DbParameter[] p /// The parameters to use in the command. /// The function delegate that will invoke a method on the resolved from the virtual method. /// A value of that is equal to the invoked method of the object. - protected virtual T ExecuteCore(IDataCommand dataCommand, DbParameter[] parameters, Func commandInvoker) + protected virtual T ExecuteCore(IDataCommand dataCommand, IDbDataParameter[] parameters, Func commandInvoker) { return InvokeCommandCore(dataCommand, parameters, commandInvoker); } - private T InvokeCommandCore(IDataCommand dataCommand, DbParameter[] parameters, Func sqlInvoker) + private T InvokeCommandCore(IDataCommand dataCommand, IDbDataParameter[] parameters, Func sqlInvoker) { T result; DbCommand command = null; @@ -555,7 +555,7 @@ private T InvokeCommandCore(IDataCommand dataCommand, DbParameter[] parameter /// The data command to execute. /// The parameters to use in the command. /// System.Data.Common.DbCommand - protected virtual DbCommand ExecuteCommandCore(IDataCommand dataCommand, params DbParameter[] parameters) + protected virtual DbCommand ExecuteCommandCore(IDataCommand dataCommand, params IDbDataParameter[] parameters) { if (dataCommand == null) throw new ArgumentNullException(nameof(dataCommand)); DbCommand command = null; @@ -586,7 +586,7 @@ private void OpenConnection(DbCommand command) /// The data command to execute. /// The parameters to use in the command. /// An instance of a implementation. - protected abstract DbCommand GetCommandCore(IDataCommand dataCommand, params DbParameter[] parameters); + protected abstract DbCommand GetCommandCore(IDataCommand dataCommand, params IDbDataParameter[] parameters); #endregion } } \ No newline at end of file diff --git a/src/Cuemon.Data/InOperatorResult.cs b/src/Cuemon.Data/InOperatorResult.cs index 337b66879..316710712 100644 --- a/src/Cuemon.Data/InOperatorResult.cs +++ b/src/Cuemon.Data/InOperatorResult.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Linq; namespace Cuemon.Data { @@ -31,6 +32,15 @@ internal InOperatorResult(IEnumerable arguments, IEnumerableThe parameters for the IN operator. public IEnumerable Parameters { get; } + /// + /// Converts the parameters for the IN operator to an . + /// + /// An array of . + public IDbDataParameter[] ToParametersArray() + { + return Parameters.ToArray(); + } + /// /// Returns a that represents this instance. /// diff --git a/src/Cuemon.Data/QueryBuilder.cs b/src/Cuemon.Data/QueryBuilder.cs index 648d39217..467400cc7 100644 --- a/src/Cuemon.Data/QueryBuilder.cs +++ b/src/Cuemon.Data/QueryBuilder.cs @@ -155,10 +155,10 @@ public static string EncodeFragment(QueryFormat format, IEnumerable valu } /// - /// Create and returns the builded query from the specified . + /// Create and returns the query from the specified . /// /// Type of the query to create. - /// The builded T-SQL query. + /// The result of the builder as a T-SQL query. public string GetQuery(QueryType queryType) { return GetQuery(queryType, null); diff --git a/test/Cuemon.Data.SqlClient.Tests/SqlInOperatorTest.cs b/test/Cuemon.Data.SqlClient.Tests/SqlInOperatorTest.cs new file mode 100644 index 000000000..908616a79 --- /dev/null +++ b/test/Cuemon.Data.SqlClient.Tests/SqlInOperatorTest.cs @@ -0,0 +1,41 @@ +using System.Linq; +using Cuemon.Collections.Generic; +using Cuemon.Data.SqlClient.Assets; +using Cuemon.Extensions.Data; +using Cuemon.Extensions.Xunit.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Data.SqlClient +{ + public class SqlInOperatorTest : HostTest + { + private readonly SqlDataManager _manager; + + public SqlInOperatorTest(UserSecretsHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _manager = hostFixture.ServiceProvider.GetRequiredService(); + } + + [Fact] + public void ShouldSafeGuardInOperation() + { + var io = new SqlInOperator(); + var sr = io.ToSafeResult(Arguments.ToEnumerableOf("A", "B", "C")); + using (var reader = _manager.ExecuteReader(new DataCommand($"SELECT * FROM [Production].[ProductInventory] WHERE Shelf IN ({sr})"), sr.ToParametersArray())) + { + var rows = reader.ToRows(); + Assert.Equal(172, rows.Count); + Assert.Equal(sr.Arguments, sr.Parameters.Select(dbp => dbp.ParameterName)); + } + } + + public override void ConfigureServices(IServiceCollection services) + { + var cnn = Configuration.GetConnectionString("AdventureWorks"); + services.AddSingleton(new SqlDataManager(cnn)); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Data.SqlClient.Tests/SqlQueryBuilderTest.cs b/test/Cuemon.Data.SqlClient.Tests/SqlQueryBuilderTest.cs new file mode 100644 index 000000000..552028053 --- /dev/null +++ b/test/Cuemon.Data.SqlClient.Tests/SqlQueryBuilderTest.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Linq; +using Cuemon.Data.SqlClient.Assets; +using Cuemon.Extensions.Data; +using Cuemon.Extensions.Xunit.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Data.SqlClient +{ + public class SqlQueryBuilderTest : HostTest + { + private readonly SqlDataManager _manager; + + public SqlQueryBuilderTest(UserSecretsHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _manager = hostFixture.ServiceProvider.GetRequiredService(); + } + + [Fact] + public void BuildSelectQuery_ShouldSelectStateProvince() + { + var builder = new SqlQueryBuilder("[Person].[StateProvince]", new Dictionary(), new Dictionary() { { "name", null } } ) + { + EnableDirtyReads = true, + EnableReadLimit = true, + ReadLimit = 10, + EnableTableAndColumnEncapsulation = true + }; + + Assert.True(builder.EnableDirtyReads); + Assert.True(builder.EnableReadLimit); + Assert.True(builder.EnableTableAndColumnEncapsulation); + Assert.Equal(10, builder.ReadLimit); + + var sql = builder.GetQuery(QueryType.Select); + + Assert.Contains("WITH(NOLOCK)", sql); + Assert.Contains("TOP 10", sql); + + using (var reader = _manager.ExecuteReader(new DataCommand(sql))) + { + var rows = reader.ToRows(); + Assert.Equal(10, rows.Count); + + TestOutput.WriteLine(DelimitedString.Create(rows.Select(dtr => dtr["name"]))); + + } + } + + public override void ConfigureServices(IServiceCollection services) + { + var cnn = Configuration.GetConnectionString("AdventureWorks"); + services.AddSingleton(new SqlDataManager(cnn)); + } + } +} \ No newline at end of file From edae7e85233fd2d614ad48626405d0892f979c98 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 12 Oct 2020 21:09:18 +0200 Subject: [PATCH 285/385] Renamed due to naming conflict with new net core. --- .../Builder/MiddlewareBuilderFactory.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/Cuemon.AspNetCore/Builder/MiddlewareBuilderFactory.cs diff --git a/src/Cuemon.AspNetCore/Builder/MiddlewareBuilderFactory.cs b/src/Cuemon.AspNetCore/Builder/MiddlewareBuilderFactory.cs new file mode 100644 index 000000000..d34a94132 --- /dev/null +++ b/src/Cuemon.AspNetCore/Builder/MiddlewareBuilderFactory.cs @@ -0,0 +1,38 @@ +using System; +using Cuemon.AspNetCore.Infrastructure; +using Microsoft.AspNetCore.Builder; + +namespace Cuemon.AspNetCore.Builder +{ + /// + /// Provides support for creating, using and configuring or implementations. + /// + public static class MiddlewareBuilderFactory + { + /// + /// Adds a middleware type to the application request pipeline. + /// + /// The type of the middleware. + /// The instance. + /// The instance. + public static IApplicationBuilder UseMiddleware(IApplicationBuilder builder) where TMiddleware : MiddlewareCore + { + return builder.UseMiddleware(); + } + + /// + /// Adds a configurable middleware type to the application request pipeline. + /// + /// The type of the configurable middleware. + /// The type of the delegate setup. + /// The instance. + /// The which need to be configured. + /// The instance. + public static IApplicationBuilder UseMiddlewareConfigurable(IApplicationBuilder builder, Action setup = null) + where TMiddleware : ConfigurableMiddlewareCore + where TOptions : class, new() + { + return setup == null ? builder.UseMiddleware() : builder.UseMiddleware(setup); + } + } +} \ No newline at end of file From 93bf8058003a2c34c432712445549b13ea7d7c39 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 12 Oct 2020 21:09:45 +0200 Subject: [PATCH 286/385] Consequence of raname. --- .../Builder/ApplicationBuilderFactory.cs | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 src/Cuemon.AspNetCore/Builder/ApplicationBuilderFactory.cs diff --git a/src/Cuemon.AspNetCore/Builder/ApplicationBuilderFactory.cs b/src/Cuemon.AspNetCore/Builder/ApplicationBuilderFactory.cs deleted file mode 100644 index a2520ff06..000000000 --- a/src/Cuemon.AspNetCore/Builder/ApplicationBuilderFactory.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using Cuemon.AspNetCore.Infrastructure; -using Microsoft.AspNetCore.Builder; - -namespace Cuemon.AspNetCore.Builder -{ - /// - /// Provides support for creating, using and configuring or implementations. - /// - public static class ApplicationBuilderFactory - { - /// - /// Adds a middleware type to the application request pipeline. - /// - /// The type of the middleware. - /// The instance. - /// The instance. - public static IApplicationBuilder UseMiddleware(IApplicationBuilder builder) where TMiddleware : MiddlewareCore - { - return builder.UseMiddleware(); - } - - /// - /// Adds a configurable middleware type to the application request pipeline. - /// - /// The type of the configurable middleware. - /// The type of the delegate setup. - /// The instance. - /// The which need to be configured. - /// The instance. - public static IApplicationBuilder UseMiddlewareConfigurable(IApplicationBuilder builder, Action setup = null) - where TMiddleware : ConfigurableMiddlewareCore - where TOptions : class, new() - { - return setup == null ? builder.UseMiddleware() : builder.UseMiddleware(setup); - } - } -} \ No newline at end of file From 0d35635688f4791a869168ce7ee34e24ca65d9dc Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 12 Oct 2020 21:12:19 +0200 Subject: [PATCH 287/385] Fixed a bug introduced with new version of net core. OnStarting is reserved to headers and the likes. --- .../Cuemon.AspNetCore.csproj | 4 ++++ .../Headers/UserAgentSentinelMiddleware.cs | 20 ++++++++----------- .../Http/Headers/UserAgentSentinelOptions.cs | 7 +++---- .../AspNetCoreInfrastructure.cs | 6 +----- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj index 68ad01a70..c7a90c06a 100644 --- a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj +++ b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj @@ -31,4 +31,8 @@ + + + + \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs b/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs index 9722f1765..411001827 100644 --- a/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs +++ b/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs @@ -37,21 +37,17 @@ public UserAgentSentinelMiddleware(RequestDelegate next, ActionA task that represents the execution of this middleware. public override async Task InvokeAsync(HttpContext context) { - var exception = false; - try + await AspNetCoreInfrastructure.InvokeUserAgentSentinelAsync(context, Options, async (message, response) => { - await AspNetCoreInfrastructure.InvokeUserAgentSentinelAsync(context, Options, async (message, response) => + context.Response.OnStarting(() => { - response.StatusCode = (int) message.StatusCode; Decorator.Enclose(response.Headers).AddOrUpdateHeaders(message.Headers); - await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); - }).ConfigureAwait(false); - } - catch (UserAgentException) - { - exception = true; - } - if (!exception) { await Next(context).ConfigureAwait(false); } + return Task.CompletedTask; + }); + response.StatusCode = (int)message.StatusCode; + await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); + }).ConfigureAwait(false); + await Next(context).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelOptions.cs b/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelOptions.cs index 8d7a6ede9..4fcea242e 100644 --- a/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelOptions.cs +++ b/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelOptions.cs @@ -14,7 +14,7 @@ public class UserAgentSentinelOptions /// /// Initializes a new instance of the class. /// - /// + /// /// The following table shows the initial property values for an instance of . /// /// @@ -53,7 +53,6 @@ public class UserAgentSentinelOptions /// public UserAgentSentinelOptions() { - UseGenericResponse = false; BadRequestMessage = "The requirements of the HTTP User-Agent header was not met."; ForbiddenMessage = "The HTTP User-Agent specified was rejected."; AllowedUserAgents = new List(); @@ -65,14 +64,14 @@ public UserAgentSentinelOptions() AllowedUserAgents.Count > 0 && !AllowedUserAgents.Any(allowedUserAgent => userAgent.Equals(allowedUserAgent, StringComparison.OrdinalIgnoreCase)); - if (userAgentIsNullOrWhiteSpace || (forbidden && UseGenericResponse)) + if (userAgentIsNullOrWhiteSpace || forbidden && UseGenericResponse) { return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(BadRequestMessage) }; } - + if (forbidden) { return new HttpResponseMessage(HttpStatusCode.Forbidden) diff --git a/src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs b/src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs index 844dfe696..83b41f522 100644 --- a/src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs +++ b/src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs @@ -24,11 +24,7 @@ public static async Task InvokeUserAgentSentinelAsync(HttpContext context, UserA var message = options.ResponseBroker?.Invoke(userAgent); if (message != null) { - context.Response.OnStarting(() => - { - transformer?.Invoke(message, context.Response); - return Task.CompletedTask; - }); + transformer?.Invoke(message, context.Response); throw new UserAgentException((int)message.StatusCode, await message.Content.ReadAsStringAsync().ConfigureAwait(false)); } } From c9e6a517c4be89eaf974f6b8a7df15532c3f1f40 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 12 Oct 2020 21:12:43 +0200 Subject: [PATCH 288/385] Consequence changes. --- .../BasicAuthenticationMiddleware.cs | 2 +- .../DigestAccessAuthenticationMiddleware.cs | 2 +- .../HmacAuthenticationMiddleware.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs index 21e7d4341..bc829930c 100644 --- a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs @@ -99,7 +99,7 @@ public static class BasicAuthenticationBuilderExtension /// A reference to this instance after the operation has completed. public static IApplicationBuilder UseBasicAuthentication(this IApplicationBuilder builder, Action setup = null) { - return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs index a9b201ba6..2f5e86897 100644 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs @@ -177,7 +177,7 @@ public static class DigestAccessAuthenticationBuilderExtension /// A reference to this instance after the operation has completed. public static IApplicationBuilder UseDigestAccessAuthentication(this IApplicationBuilder builder, Action setup = null) { - return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs index 705382655..d81d25d19 100644 --- a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs @@ -103,7 +103,7 @@ public static class HmacAuthenticationBuilderExtension /// A reference to this instance after the operation has completed. public static IApplicationBuilder UseHmacAuthentication(this IApplicationBuilder builder, Action setup = null) { - return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); } } } \ No newline at end of file From 580cb48bd108b5363d856f1fd49bd8d42bd8b349 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 12 Oct 2020 21:13:53 +0200 Subject: [PATCH 289/385] Typo fix. --- src/Cuemon.Data.Integrity/IEntityDataIntegrity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Data.Integrity/IEntityDataIntegrity.cs b/src/Cuemon.Data.Integrity/IEntityDataIntegrity.cs index 77d65c325..112da9a58 100644 --- a/src/Cuemon.Data.Integrity/IEntityDataIntegrity.cs +++ b/src/Cuemon.Data.Integrity/IEntityDataIntegrity.cs @@ -1,7 +1,7 @@ namespace Cuemon.Data.Integrity { /// - /// An interface that represents the integrity od data that is normally associated with an entity/resource. + /// An interface that represents the integrity of data that is normally associated with an entity/resource. /// /// public interface IEntityDataIntegrity : IDataIntegrity From dcd32a8e8853999f1758e7dcf79b634f8f95c4e8 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 12 Oct 2020 21:14:13 +0200 Subject: [PATCH 290/385] Consequence changes. --- .../Builder/ApplicationBuilderExtensions.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs index 474e3bce4..37548b8e4 100644 --- a/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs @@ -21,7 +21,7 @@ public static class ApplicationBuilderExtensions /// Default HTTP header name is X-Hosting-Environment. public static IApplicationBuilder UseHostingEnvironment(this IApplicationBuilder builder, Action setup = null) { - return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); } /// @@ -33,7 +33,7 @@ public static IApplicationBuilder UseHostingEnvironment(this IApplicationBuilder /// Default HTTP header name is X-Correlation-ID. public static IApplicationBuilder UseCorrelationIdentifier(this IApplicationBuilder builder, Action setup = null) { - return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); } /// @@ -45,7 +45,7 @@ public static IApplicationBuilder UseCorrelationIdentifier(this IApplicationBuil /// Default HTTP header name is X-Request-ID. public static IApplicationBuilder UseRequestIdentifier(this IApplicationBuilder builder, Action setup = null) { - return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); } /// @@ -56,7 +56,7 @@ public static IApplicationBuilder UseRequestIdentifier(this IApplicationBuilder /// A reference to this instance after the operation has completed. public static IApplicationBuilder UseUserAgentSentinel(this IApplicationBuilder builder, Action setup = null) { - return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); } /// @@ -67,7 +67,7 @@ public static IApplicationBuilder UseUserAgentSentinel(this IApplicationBuilder /// A reference to this instance after the operation has completed. public static IApplicationBuilder UseThrottlingSentinel(this IApplicationBuilder builder, Action setup) { - return ApplicationBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); } } } \ No newline at end of file From 498a55817bcedb2d33f7f11285db12c89dd682b3 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 12 Oct 2020 21:14:49 +0200 Subject: [PATCH 291/385] New assembly tailored unit testing for ASP.NET Core. --- .../ApplicationBuilderExtensions.cs | 23 +++++ .../AspNetCoreHostFixture.cs | 98 +++++++++++++++++++ .../AspNetCoreHostFixtureExtensions.cs | 10 ++ .../AspNetCoreHostTest.cs | 72 ++++++++++++++ ...Extensions.Xunit.AspNetCore.Hosting.csproj | 26 +++++ ...Extensions.Xunit.Hosting.AspNetCore.csproj | 23 +++++ .../Http/FakeHttpContextAccessor.cs | 38 +++++++ .../Http/Features/FakeHttpResponseFeature.cs | 54 ++++++++++ .../Features/FakeHttpResponseMiddleware.cs | 33 +++++++ .../IAspNetCoreHostFixture.cs | 26 +++++ .../IMiddlewareTest.cs | 11 +++ .../IPipelineTest.cs | 17 ++++ .../MiddlewareAspNetCoreHostTest.cs | 41 ++++++++ .../MiddlewareTestFactory.cs | 27 +++++ 14 files changed, 499 insertions(+) create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/ApplicationBuilderExtensions.cs create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixtureExtensions.cs create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostTest.cs create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon - Backup.Extensions.Xunit.AspNetCore.Hosting.csproj create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/FakeHttpContextAccessor.cs create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseFeature.cs create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseMiddleware.cs create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IAspNetCoreHostFixture.cs create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IMiddlewareTest.cs create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IPipelineTest.cs create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareAspNetCoreHostTest.cs create mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareTestFactory.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/ApplicationBuilderExtensions.cs new file mode 100644 index 000000000..d8ac99df1 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/ApplicationBuilderExtensions.cs @@ -0,0 +1,23 @@ +using Cuemon.AspNetCore.Builder; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Builder; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +{ + /// + /// Extension methods for the interface. + /// + public static class ApplicationBuilderExtensions + { + /// + /// Adds a to the request execution pipeline. + /// + /// The type that provides the mechanisms to configure an application’s request pipeline. + /// A reference to this instance after the operation has completed. + /// + public static IApplicationBuilder UseFakeHttpResponseTrigger(this IApplicationBuilder builder) + { + return MiddlewareBuilderFactory.UseMiddleware(builder); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs new file mode 100644 index 000000000..8cd87adbd --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs @@ -0,0 +1,98 @@ +using System; +using System.IO; +using System.Linq; +using Cuemon.Reflection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +{ + /// + /// Provides a default implementation of the interface. + /// + /// + /// + public class AspNetCoreHostFixture : HostFixture, IAspNetCoreHostFixture + { + /// + /// Initializes a new instance of the class. + /// + public AspNetCoreHostFixture() + { + } + + /// + /// Creates and configures the of this instance. + /// + /// The object that inherits from . + /// was added to support those cases where the caller is required in the host configuration. + /// + /// is null. + /// + /// + /// is not assignable from . + /// + public override void ConfigureHost(Test hostTest) + { + var hostTestType = hostTest?.GetType(); + Validator.ThrowIfNull(hostTest, nameof(hostTest)); + Validator.ThrowIfNotContainsType(hostTestType, nameof(hostTestType), $"{nameof(hostTest)} is not assignable from AspNetCoreHostTest.", typeof(AspNetCoreHostTest<>)); + + var server = new TestServer(new WebHostBuilder() + .ConfigureAppConfiguration((context, config) => + { + config.AddEnvironmentVariables("ASPNETCORE_"); + config.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) + .AddEnvironmentVariables(); + }) + .ConfigureServices((context, services) => + { + var flags = new MemberReflection(excludeStatic: true, excludePublic: true).Flags; + var hostTestTypeBase = Decorator.Enclose(hostTestType).GetInheritedTypes().Single(t => t.BaseType == typeof(Test)); + hostTestTypeBase.GetField("_configuration", flags).SetValue(hostTest, context.Configuration); + hostTestTypeBase.GetField("_hostingEnvironment", flags).SetValue(hostTest, context.HostingEnvironment); + + Configuration = context.Configuration; + HostingEnvironment = context.HostingEnvironment; + ConfigureServicesCallback(services); + ServiceProvider = services.BuildServiceProvider(); + }) + .Configure(app => + { + ConfigureApplicationCallback(app); + Application = app; + } + )); + + var host = server.Host; + + host.Start(); + + Host = host; + } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public new IWebHost Host { get; private set; } + + /// + /// Gets or sets the delegate that configures the HTTP request pipeline. + /// + /// The delegate that configures the HTTP request pipeline. + public Action ConfigureApplicationCallback { get; set; } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IApplicationBuilder Application { get; protected set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixtureExtensions.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixtureExtensions.cs new file mode 100644 index 000000000..986557639 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixtureExtensions.cs @@ -0,0 +1,10 @@ +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +{ + internal static class AspNetCoreHostFixtureExtensions + { + internal static bool HasValidState(this IAspNetCoreHostFixture fixture) + { + return fixture.ConfigureServicesCallback != null && fixture.Host != null && fixture.ServiceProvider != null && fixture.Application != null; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostTest.cs new file mode 100644 index 000000000..a92e2ef1f --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostTest.cs @@ -0,0 +1,72 @@ +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.Abstractions; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +{ + /// + /// Represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection and depends on ASP.NET Core, should derive. + /// + /// The type of the object that implements the interface. + /// + /// + public abstract class AspNetCoreHostTest : HostTest where T : class, IAspNetCoreHostFixture + { + /// + /// Initializes a new instance of the class. + /// + /// An implementation of the interface. + /// An implementation of the interface. + protected AspNetCoreHostTest(T aspNetCoreHostFixture, ITestOutputHelper output = null) : base(aspNetCoreHostFixture, output) + { + } + + /// + /// Initializes the specified host fixture. + /// + /// The host fixture to initialize. + protected override void InitializeHostFixture(T hostFixture) + { + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureServicesCallback = ConfigureServices; + hostFixture.ConfigureApplicationCallback = ConfigureApplication; + hostFixture.ConfigureHost(this); + } + Host = hostFixture.Host; + ServiceProvider = hostFixture.ServiceProvider; + Application = hostFixture.Application; + } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public new IWebHost Host { get; protected set; } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IApplicationBuilder Application { get; protected set; } + + /// + /// Adds services to the container. + /// + /// The collection of service descriptors. + public override void ConfigureServices(IServiceCollection services) + { + services.AddTransient(); + } + + /// + /// Configures the HTTP request pipeline. + /// + /// The type that provides the mechanisms to configure the HTTP request pipeline. + public abstract void ConfigureApplication(IApplicationBuilder app); + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon - Backup.Extensions.Xunit.AspNetCore.Hosting.csproj b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon - Backup.Extensions.Xunit.AspNetCore.Hosting.csproj new file mode 100644 index 000000000..829bd9d2a --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon - Backup.Extensions.Xunit.AspNetCore.Hosting.csproj @@ -0,0 +1,26 @@ + + + + netcoreapp3.1 + 200bdf91-e7c7-4cb4-a39d-e1a5374c5602 + + + + Cuemon.Extensions.Xunit.AspNetCore.Hosting + Cuemon.Extensions.Xunit.AspNetCore.Hosting + The Cuemon.Extensions.Xunit.AspNetCore.Hosting namespace contains types that provides a uniform way of doing unit testing used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. + host-test class-fixture host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj new file mode 100644 index 000000000..33632266c --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + 200bdf91-e7c7-4cb4-a39d-e1a5374c5602 + + + + Cuemon.Extensions.Xunit.Hosting.AspNetCore + Cuemon.Extensions.Xunit.Hosting.AspNetCore + The Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. + host-test class-fixture host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services + + + + + + + + + + + \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/FakeHttpContextAccessor.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/FakeHttpContextAccessor.cs new file mode 100644 index 000000000..78e9ca311 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/FakeHttpContextAccessor.cs @@ -0,0 +1,38 @@ +using System.IO; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http +{ + /// + /// Provides a unit test implementation of .. + /// + /// + public class FakeHttpContextAccessor : IHttpContextAccessor + { + private HttpContext _httpContextCurrent; + + /// + /// Initializes a new instance of the class. + /// + public FakeHttpContextAccessor() + { + var fc = new FeatureCollection(); + fc.Set(new FakeHttpResponseFeature()); + fc.Set(new HttpRequestFeature()); + _httpContextCurrent = new DefaultHttpContext(fc); + _httpContextCurrent.Response.Body = new MemoryStream(); + } + + /// + /// Gets or sets the HTTP context. + /// + /// The HTTP context. + public HttpContext HttpContext + { + get => _httpContextCurrent; + set => _httpContextCurrent = value; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseFeature.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseFeature.cs new file mode 100644 index 000000000..ca2641763 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseFeature.cs @@ -0,0 +1,54 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http.Features; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features +{ + /// + /// Represents a way to trigger . + /// + /// + public class FakeHttpResponseFeature : HttpResponseFeature + { + private bool _hasStarted; + private Func _callback; + private object _state; + + /// + /// Registers a callback to be invoked just before the response starts. This is the + /// last chance to modify the , , or + /// . + /// + /// The callback to invoke when starting the response. + /// The state to pass into the callback. + public override void OnStarting(Func callback, object state) + { + _callback = callback; + _state = state; + } + + /// + /// Gets a value indicating whether this instance has callback. + /// + /// true if this instance has callback; otherwise, false. + public bool HasOnStartingCallback => _callback != null; + + /// + /// Indicates if the response has started. If true, the , + /// , and are now immutable, and + /// OnStarting should no longer be called. + /// + /// true if this instance has started; otherwise, false. + public override bool HasStarted => _hasStarted; + + /// + /// Executes the function delegate assigned by . + /// + /// A task that represents the asynchronous operation. + public Task TriggerOnStarting() + { + _hasStarted = true; + return HasOnStartingCallback ? _callback(_state) : Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseMiddleware.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseMiddleware.cs new file mode 100644 index 000000000..abe9f56de --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseMiddleware.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using Cuemon.AspNetCore; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features +{ + /// + /// Provides a fake HTTP response middleware implementation for ASP.NET Core testing. + /// + /// + public class FakeHttpResponseMiddleware : Middleware + { + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + public FakeHttpResponseMiddleware(RequestDelegate next) : base(next) + { + } + + /// + /// Executes the . + /// + /// The context of the current request. + /// A task that represents the execution of this middleware. + public override Task InvokeAsync(HttpContext context) + { + var feature = context.Features.Get() as FakeHttpResponseFeature; + return feature?.TriggerOnStarting(); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IAspNetCoreHostFixture.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IAspNetCoreHostFixture.cs new file mode 100644 index 000000000..927e2eceb --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IAspNetCoreHostFixture.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +{ + /// + /// Provides a way to use Microsoft Dependency Injection in unit tests tailored for ASP.NET Core. + /// + /// + public interface IAspNetCoreHostFixture : IHostFixture, IPipelineTest + { + /// + /// Gets or sets the delegate that configures the HTTP request pipeline. + /// + /// The delegate that configures the HTTP request pipeline. + Action ConfigureApplicationCallback { get; set; } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public new IWebHost Host { get; } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IMiddlewareTest.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IMiddlewareTest.cs new file mode 100644 index 000000000..3d00f23a0 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IMiddlewareTest.cs @@ -0,0 +1,11 @@ +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +{ + /// + /// Represents the members needed for ASP.NET Core middleware testing. + /// + /// + /// + public interface IMiddlewareTest : IServiceTest, IPipelineTest + { + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IPipelineTest.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IPipelineTest.cs new file mode 100644 index 000000000..7988c100d --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IPipelineTest.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +{ + /// + /// Represents the members needed for ASP.NET Core pipeline testing. + /// + public interface IPipelineTest + { + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IApplicationBuilder Application { get; } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareAspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareAspNetCoreHostTest.cs new file mode 100644 index 000000000..9736e3487 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareAspNetCoreHostTest.cs @@ -0,0 +1,41 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +{ + internal class MiddlewareAspNetCoreHostTest : AspNetCoreHostTest, IMiddlewareTest + { + private readonly Action _pipelineConfigurator; + private readonly Action _serviceConfigurator; + + internal MiddlewareAspNetCoreHostTest(Action pipelineConfigurator, Action serviceConfigurator, AspNetCoreHostFixture hostFixture) : base(hostFixture) + { + _pipelineConfigurator = pipelineConfigurator; + _serviceConfigurator = serviceConfigurator; + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureServicesCallback = ConfigureServices; + hostFixture.ConfigureApplicationCallback = ConfigureApplication; + hostFixture.ConfigureHost(this); + } + Host = hostFixture.Host; + ServiceProvider = hostFixture.ServiceProvider; + Application = hostFixture.Application; + } + + protected override void InitializeHostFixture(AspNetCoreHostFixture hostFixture) + { + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + _pipelineConfigurator(app); + } + + public override void ConfigureServices(IServiceCollection services) + { + _serviceConfigurator(services); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareTestFactory.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareTestFactory.cs new file mode 100644 index 000000000..f32bebff7 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareTestFactory.cs @@ -0,0 +1,27 @@ +using System; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +{ + /// + /// Provides a set of static methods for ASP.NET Core middleware unit testing. + /// + public static class MiddlewareTestFactory + { + /// + /// Creates and returns an implementation. + /// + /// The which may be configured. + /// The which may be configured. + /// An instance of an implementation. + public static IMiddlewareTest CreateMiddlewareTest(Action pipelineSetup = null, Action serviceSetup = null) + { + pipelineSetup ??= app => app.UseFakeHttpResponseTrigger(); + serviceSetup ??= services => services.AddScoped(); + return new MiddlewareAspNetCoreHostTest(pipelineSetup, serviceSetup, new AspNetCoreHostFixture()); + } + } +} \ No newline at end of file From dfd9e070d8ef078b30264b51a89520f0bd1b4a31 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 12 Oct 2020 21:15:27 +0200 Subject: [PATCH 292/385] Changes to accomodate other test assemblies. --- .../Cuemon.Extensions.Xunit.Hosting.csproj | 10 +++++----- .../HostFixture.cs | 8 ++++---- .../HostTest.cs | 20 +++++++++++++------ .../IHostFixture.cs | 10 ++-------- .../IServiceTest.cs | 17 ++++++++++++++++ 5 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/IServiceTest.cs diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj index 31e9d5b2d..9921f36d7 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj @@ -13,11 +13,11 @@ - - - - - + + + + + diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs index 5b87c7a24..40fa7ad81 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Linq; -using System.Reflection; using Cuemon.Reflection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -55,6 +54,7 @@ public virtual void ConfigureHost(Test hostTest) var hostTestTypeBase = Decorator.Enclose(hostTestType).GetInheritedTypes().Single(t => t.BaseType == typeof(Test)); hostTestTypeBase.GetField("_configuration", flags).SetValue(hostTest, context.Configuration); hostTestTypeBase.GetField("_hostingEnvironment", flags).SetValue(hostTest, context.HostingEnvironment); + Configuration = context.Configuration; HostingEnvironment = context.HostingEnvironment; ConfigureServicesCallback(services); @@ -84,20 +84,20 @@ public virtual void ConfigureHost(Test hostTest) /// Gets the initialized by this instance. /// /// The initialized by this instance. - public IConfiguration Configuration { get; private set; } + public IConfiguration Configuration { get; protected set; } #if NETSTANDARD /// /// Gets the initialized by this instance. /// /// The initialized by this instance. - public IHostingEnvironment HostingEnvironment { get; private set; } + public IHostingEnvironment HostingEnvironment { get; protected set; } #elif NETCOREAPP /// /// Gets the initialized by this instance. /// /// The initialized by this instance. - public IHostEnvironment HostingEnvironment { get; private set; } + public IHostEnvironment HostingEnvironment { get; protected set; } #endif /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs index 409cb8fb9..bf27d277d 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs @@ -7,7 +7,6 @@ namespace Cuemon.Extensions.Xunit.Hosting { - /// /// Represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection, should derive. /// @@ -29,16 +28,25 @@ public abstract class HostTest : Test, IClassFixture where T : class, IHos /// /// An implementation of the interface. /// An implementation of the interface. - protected HostTest(IHostFixture hostFixture, ITestOutputHelper output = null) : base(output) + protected HostTest(T hostFixture, ITestOutputHelper output = null) : base(output) { Validator.ThrowIfNull(hostFixture, nameof(hostFixture)); + InitializeHostFixture(hostFixture); + if (_configuration == null) { _configuration = hostFixture.Configuration; } + if (_hostingEnvironment == null) { _hostingEnvironment = hostFixture.HostingEnvironment; } + } + + /// + /// Initializes the specified host fixture. + /// + /// The host fixture to initialize. + protected virtual void InitializeHostFixture(T hostFixture) + { if (!hostFixture.HasValidState()) { hostFixture.ConfigureServicesCallback = ConfigureServices; hostFixture.ConfigureHost(this); } - if (_configuration == null) { _configuration = hostFixture.Configuration; } - if (_hostingEnvironment == null) { _hostingEnvironment = hostFixture.HostingEnvironment; } Host = hostFixture.Host; ServiceProvider = hostFixture.ServiceProvider; } @@ -47,13 +55,13 @@ protected HostTest(IHostFixture hostFixture, ITestOutputHelper output = null) : /// Gets the initialized by the . /// /// The initialized by the . - public IHost Host { get; } + public IHost Host { get; protected set; } /// /// Gets the initialized by the . /// /// The initialized by the . - public IServiceProvider ServiceProvider { get; } + public IServiceProvider ServiceProvider { get; protected set; } /// /// Gets the initialized by the . diff --git a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs index 8df22df7a..e50998db2 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs @@ -9,26 +9,20 @@ namespace Cuemon.Extensions.Xunit.Hosting /// Provides a way to use Microsoft Dependency Injection in unit tests. /// /// - public interface IHostFixture : IDisposable + public interface IHostFixture : IServiceTest, IDisposable { /// /// Gets or sets the delegate that adds services to the container. /// /// The delegate that adds services to the container. Action ConfigureServicesCallback { get; set; } - + /// /// Gets the initialized by the . /// /// The initialized by the . IHost Host { get; } - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - IServiceProvider ServiceProvider { get; } - /// /// Gets the initialized by the . /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting/IServiceTest.cs b/src/Cuemon.Extensions.Xunit.Hosting/IServiceTest.cs new file mode 100644 index 000000000..27ccf4816 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/IServiceTest.cs @@ -0,0 +1,17 @@ +using System; +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Extensions.Xunit.Hosting +{ + /// + /// Represents the members needed for ASP.NET Core services testing. + /// + public interface IServiceTest + { + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IServiceProvider ServiceProvider { get; } + } +} \ No newline at end of file From 01ca9434fe48d33dca042fc086b8697f8f37abf5 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 12 Oct 2020 21:15:51 +0200 Subject: [PATCH 293/385] Unit test of ASP.NET Core components. --- .../Cuemon.AspNetCore.Tests.csproj | 7 + .../HostingEnvironmentMiddlewareTest.cs | 51 +++++ .../CorrelationIdentifierMiddlewareTest.cs | 67 +++++++ .../RequestIdentifierMiddlewareTest.cs | 76 ++++++++ .../UserAgentSentinelMiddlewareTest.cs | 177 ++++++++++++++++++ 5 files changed, 378 insertions(+) create mode 100644 test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs create mode 100644 test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs create mode 100644 test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs create mode 100644 test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs diff --git a/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj b/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj index 8f83561f3..fe70d327a 100644 --- a/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj +++ b/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj @@ -5,7 +5,14 @@ + + + + + + + \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs new file mode 100644 index 000000000..124404c20 --- /dev/null +++ b/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs @@ -0,0 +1,51 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; +using System.Linq; +using System.Threading.Tasks; +using Cuemon.Extensions.AspNetCore.Builder; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Hosting +{ + public class HostingEnvironmentMiddlewareTest : AspNetCoreHostTest + { + private readonly IServiceProvider _provider; + private readonly IApplicationBuilder _pipeline; + + public HostingEnvironmentMiddlewareTest(AspNetCoreHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _pipeline = hostFixture.Application; + _provider = hostFixture.ServiceProvider; + } + + [Fact] + public async Task InvokeAsync_ShouldHaveHostingEnvironmentHeader_ConfiguredByIOptions() + { + var context = _provider.GetRequiredService().HttpContext; + var options = _provider.GetRequiredService>(); + var pipeline = _pipeline.Build(); + + await pipeline(context); + + Assert.True(context.Response.Headers.TryGetValue(options.Value.HeaderName, out var xHostingEnvironmentHeader)); + Assert.Equal(HostingEnvironment.EnvironmentName, xHostingEnvironmentHeader.Single()); + } + + public override void ConfigureServices(IServiceCollection services) + { + base.ConfigureServices(services); + services.Configure(o => o.HeaderName = "X-Environment"); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + app.UseHostingEnvironment(); + app.UseFakeHttpResponseTrigger(); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs new file mode 100644 index 000000000..ef5943ae5 --- /dev/null +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Cuemon.Extensions.AspNetCore.Builder; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Text; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Http.Headers +{ + public class CorrelationIdentifierMiddlewareTest : AspNetCoreHostTest + { + private readonly IServiceProvider _provider; + private readonly IApplicationBuilder _pipeline; + + public CorrelationIdentifierMiddlewareTest(AspNetCoreHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _pipeline = hostFixture.Application; + _provider = hostFixture.ServiceProvider; + } + + [Fact] + public async Task InvokeAsync_ShouldCreateNewCorrelationIdHeader_ConfiguredByDefault() + { + var context = _provider.GetRequiredService().HttpContext; + var options = _provider.GetRequiredService>(); + var pipeline = _pipeline.Build(); + + await pipeline(context); + + Assert.True(context.Response.Headers.TryGetValue(options.Value.HeaderName, out var xCorrelationIdHeader)); + Assert.True(ParserFactory.FromGuid().TryParse(xCorrelationIdHeader.Single(), out var correlationId, o => o.Formats = GuidFormats.N)); + + TestOutput.WriteLine(correlationId.ToString("N")); + } + + [Fact] + public async Task InvokeAsync_ShouldRelayCorrelationIdHeader_ConfiguredByDefault() + { + var expected = "072a8d5aa1cc4a16bf04132482748243"; + var context = _provider.GetRequiredService().HttpContext; + var options = _provider.GetRequiredService>(); + var pipeline = _pipeline.Build(); + + context.Request.Headers.Add(options.Value.HeaderName, expected); + + await pipeline(context); + + Assert.True(context.Response.Headers.TryGetValue(options.Value.HeaderName, out var xCorrelationIdHeader)); + Assert.True(ParserFactory.FromGuid().TryParse(xCorrelationIdHeader.Single(), out var correlationId, o => o.Formats = GuidFormats.N)); + Assert.Equal(expected, correlationId.ToString("N")); + + TestOutput.WriteLine(expected); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + app.UseCorrelationIdentifier(); + app.UseFakeHttpResponseTrigger(); + } + } +} diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs new file mode 100644 index 000000000..198d5b465 --- /dev/null +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs @@ -0,0 +1,76 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Cuemon.Extensions.AspNetCore.Builder; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Messaging; +using Cuemon.Text; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Http.Headers +{ + public class RequestIdentifierMiddlewareTest : AspNetCoreHostTest + { + private readonly IServiceProvider _provider; + private readonly IApplicationBuilder _pipeline; + + public RequestIdentifierMiddlewareTest(AspNetCoreHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _pipeline = hostFixture.Application; + _provider = hostFixture.ServiceProvider; + } + + [Fact] + public async Task InvokeAsync_ShouldCreateNewRequestIdHeader_ConfiguredByDelegate() + { + var context = _provider.GetRequiredService().HttpContext; + var options = _provider.GetRequiredService>(); + var pipeline = _pipeline.Build(); + + await pipeline(context); + + Assert.True(context.Response.Headers.TryGetValue(options.Value.HeaderName, out var xRequestIdHeader)); + + var requestId = xRequestIdHeader.Single(); + + Assert.False(ParserFactory.FromGuid().TryParse(requestId, out _, o => o.Formats = GuidFormats.N)); + Assert.Equal(32, requestId.Length); + + TestOutput.WriteLine(requestId); + } + + [Fact] + public async Task InvokeAsync_ShouldIgnoreExistingRequestIdHeader_ConfiguredByDefault() + { + var expected = "072a8d5aa1cc4a16bf04132482748243"; + var context = _provider.GetRequiredService().HttpContext; + var options = _provider.GetRequiredService>(); + var pipeline = _pipeline.Build(); + + context.Request.Headers.Add(options.Value.HeaderName, expected); + + await pipeline(context); + + Assert.True(context.Response.Headers.TryGetValue(options.Value.HeaderName, out var xRequestIdHeader)); + + var requestId = xRequestIdHeader.Single(); + + Assert.False(ParserFactory.FromGuid().TryParse(requestId, out _, o => o.Formats = GuidFormats.N)); + Assert.NotEqual(expected, requestId); + Assert.Equal(32, requestId.Length); + + TestOutput.WriteLine(requestId); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + app.UseRequestIdentifier(o => o.RequestProvider = () => DynamicRequest.Create(Generate.RandomString(32, Alphanumeric.PunctuationMarks, Alphanumeric.Numbers))); + app.UseFakeHttpResponseTrigger(); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs new file mode 100644 index 000000000..37675d684 --- /dev/null +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs @@ -0,0 +1,177 @@ +using System.Linq; +using System.Threading.Tasks; +using Cuemon.Extensions.AspNetCore.Builder; +using Cuemon.Extensions.IO; +using Cuemon.Extensions.Xunit; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Http.Headers +{ + public class UserAgentSentinelMiddlewareTest : Test + { + public UserAgentSentinelMiddlewareTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task InvokeAsync_ShouldThrowUserAgentException_BadRequest() + { + var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseUserAgentSentinel(); + app.UseFakeHttpResponseTrigger(); + }, services => + { + services.Configure(o => + { + o.RequireUserAgentHeader = true; + }); + services.AddScoped(); + }); + + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + var uae = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(uae.Message, options.Value.BadRequestMessage); + Assert.Equal(uae.StatusCode, StatusCodes.Status400BadRequest); + + Assert.True(options.Value.RequireUserAgentHeader); + Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode); + Assert.Equal(options.Value.BadRequestMessage, context.Response.Body.ToEncodedString()); + } + + [Fact] + public async Task InvokeAsync_ShouldThrowUserAgentException_Forbidden() + { + var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseUserAgentSentinel(); + app.UseFakeHttpResponseTrigger(); + }, services => + { + services.Configure(o => + { + o.RequireUserAgentHeader = true; + o.ValidateUserAgentHeader = true; + o.AllowedUserAgents.Add("Cuemon-Agent"); + }); + services.AddScoped(); + }); + + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + context.Request.Headers.Add(HeaderNames.UserAgent, "Invalid-Agent"); + + var uae = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(uae.Message, options.Value.ForbiddenMessage); + Assert.Equal(uae.StatusCode, StatusCodes.Status403Forbidden); + + Assert.True(options.Value.RequireUserAgentHeader); + Assert.True(options.Value.ValidateUserAgentHeader); + Assert.True(options.Value.AllowedUserAgents.Any()); + Assert.Equal(StatusCodes.Status403Forbidden, context.Response.StatusCode); + Assert.Equal(options.Value.ForbiddenMessage, context.Response.Body.ToEncodedString()); + } + + [Fact] + public async Task InvokeAsync_ShouldThrowUserAgentException_BadRequest_BecauseOfUseGenericResponse() + { + var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseUserAgentSentinel(); + app.UseFakeHttpResponseTrigger(); + }, services => + { + services.Configure(o => + { + o.RequireUserAgentHeader = true; + o.ValidateUserAgentHeader = true; + o.UseGenericResponse = true; + o.AllowedUserAgents.Add("Cuemon-Agent"); + }); + services.AddScoped(); + }); + + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + context.Request.Headers.Add(HeaderNames.UserAgent, "Invalid-Agent"); + + var uae = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(uae.Message, options.Value.BadRequestMessage); + Assert.Equal(uae.StatusCode, StatusCodes.Status400BadRequest); + + Assert.True(options.Value.RequireUserAgentHeader); + Assert.True(options.Value.ValidateUserAgentHeader); + Assert.True(options.Value.UseGenericResponse); + Assert.True(options.Value.AllowedUserAgents.Any()); + Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode); + Assert.Equal(options.Value.BadRequestMessage, context.Response.Body.ToEncodedString()); + } + + [Fact] + public async Task InvokeAsync_ShouldAllowRequestUnconditional() + { + var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseUserAgentSentinel(); + app.UseFakeHttpResponseTrigger(); + }); + + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + await pipeline(context); + + Assert.False(options.Value.RequireUserAgentHeader); + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } + + [Fact] + public async Task InvokeAsync_ShouldAllowRequestAfterBeingValidated() + { + var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseUserAgentSentinel(); + app.UseFakeHttpResponseTrigger(); + }, services => + { + services.Configure(o => + { + o.RequireUserAgentHeader = true; + o.ValidateUserAgentHeader = true; + o.AllowedUserAgents.Add("Cuemon-Agent"); + }); + services.AddScoped(); + }); + + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + context.Request.Headers.Add(HeaderNames.UserAgent, "Cuemon-Agent"); + + await pipeline(context); + + Assert.True(options.Value.RequireUserAgentHeader); + Assert.True(options.Value.ValidateUserAgentHeader); + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } + } +} \ No newline at end of file From e81229db4da74f496adb4566b60ee55df82cba26 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 12 Oct 2020 21:16:22 +0200 Subject: [PATCH 294/385] Test projects targets 3.1. --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 71be3e5a5..9b674d717 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -43,11 +43,11 @@ - + - netcoreapp3.0 + netcoreapp3.1 false false From 80259786f0878bdb2a80f0d4486ff0a407e595b2 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 12 Oct 2020 21:16:52 +0200 Subject: [PATCH 295/385] Cuemon.Extensions.Xunit.Hosting.AspNetCore --- Cuemon.sln | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Cuemon.sln b/Cuemon.sln index f829e576a..46e99f9c4 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -125,7 +125,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Runtime.C EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Newtonsoft.Json.Tests", "test\Cuemon.Extensions.Newtonsoft.Json.Tests\Cuemon.Extensions.Newtonsoft.Json.Tests.csproj", "{8A3E26BD-A3C4-4684-909B-1ABDFDB4108D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Data.SqlClient.Tests", "test\Cuemon.Data.SqlClient.Tests\Cuemon.Data.SqlClient.Tests.csproj", "{A9610C9E-1944-4771-A5E1-CE47ADA243D5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Data.SqlClient.Tests", "test\Cuemon.Data.SqlClient.Tests\Cuemon.Data.SqlClient.Tests.csproj", "{A9610C9E-1944-4771-A5E1-CE47ADA243D5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore", "src\Cuemon.Extensions.Xunit.AspNetCore.Hosting\Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj", "{200BDF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -373,6 +375,10 @@ Global {A9610C9E-1944-4771-A5E1-CE47ADA243D5}.Debug|Any CPU.Build.0 = Debug|Any CPU {A9610C9E-1944-4771-A5E1-CE47ADA243D5}.Release|Any CPU.ActiveCfg = Release|Any CPU {A9610C9E-1944-4771-A5E1-CE47ADA243D5}.Release|Any CPU.Build.0 = Release|Any CPU + {200BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {200BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.Build.0 = Debug|Any CPU + {200BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.ActiveCfg = Release|Any CPU + {200BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -438,6 +444,7 @@ Global {0F614FD1-BC7C-4F7F-9847-D3675614576C} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {8A3E26BD-A3C4-4684-909B-1ABDFDB4108D} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {A9610C9E-1944-4771-A5E1-CE47ADA243D5} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} + {200BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2A848386-B682-4F6D-8254-B5F6247C3054} From 19b255da8eb9a319a40991813d6479cd3b3d6f8a Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 13 Oct 2020 00:56:00 +0200 Subject: [PATCH 296/385] Removed InternalsVisibleTo and opt-in for Decorator instead (with notes that it is infrastructure code). --- .../Headers/UserAgentSentinelFilter.cs | 3 +- .../Throttling/ThrottlingSentinelFilter.cs | 4 +- .../Http/HttpContextDecoratorExtensions.cs | 98 +++++++++++++++++++ .../Headers/UserAgentSentinelMiddleware.cs | 3 +- .../ThrottlingSentinelMiddleware.cs | 21 ++-- .../AspNetCoreInfrastructure.cs | 83 ---------------- .../Properties/AssemblyInfo.cs | 6 +- 7 files changed, 111 insertions(+), 107 deletions(-) create mode 100644 src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs delete mode 100644 src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs index d416da957..524786919 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Cuemon.AspNetCore.Http; using Cuemon.AspNetCore.Http.Headers; using Cuemon.AspNetCore.Infrastructure; using Microsoft.AspNetCore.Mvc.Filters; @@ -29,7 +30,7 @@ public UserAgentSentinelFilter(IOptions setup) : base( /// A that on completion indicates the filter has executed. public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { - await AspNetCoreInfrastructure.InvokeUserAgentSentinelAsync(context.HttpContext, Options, (message, response) => + await Decorator.Enclose(context.HttpContext).InvokeUserAgentSentinelAsync(Options, (message, response) => { response.StatusCode = (int) message.StatusCode; Decorator.Enclose(response.Headers).AddOrUpdateHeaders(message.Headers); diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelFilter.cs index ba64209c6..561fd254d 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelFilter.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; +using Cuemon.AspNetCore.Http; using Cuemon.AspNetCore.Http.Headers; using Cuemon.AspNetCore.Http.Throttling; -using Cuemon.AspNetCore.Infrastructure; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Options; @@ -34,7 +34,7 @@ public ThrottlingSentinelFilter(IOptions setup, IThro /// A that on completion indicates the filter has executed. public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { - await AspNetCoreInfrastructure.InvokeThrottlerSentinelAsync(context.HttpContext, ThrottlingCache, Options, (message, response) => + await Decorator.Enclose(context.HttpContext).InvokeThrottlerSentinelAsync(ThrottlingCache, Options, (message, response) => { response.StatusCode = (int) message.StatusCode; Decorator.Enclose(response.Headers).AddOrUpdateHeaders(message.Headers); diff --git a/src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs b/src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs new file mode 100644 index 000000000..fe53d6e7d --- /dev/null +++ b/src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs @@ -0,0 +1,98 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.AspNetCore.Http.Headers; +using Cuemon.AspNetCore.Http.Throttling; +using Cuemon.Collections.Generic; +using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; + +namespace Cuemon.AspNetCore.Http +{ + /// + /// Extension methods for the class tailored to adhere the decorator pattern. + /// This API supports the product infrastructure and is not intended to be used directly from your code. + /// + /// + /// + public static class HttpContextDecoratorExtensions + { + private static readonly SemaphoreSlim ThrottleLocker = new SemaphoreSlim(1); + + /// + /// Common throttler operation logic for ASP.NET Core and ASP.NET Core MVC. Not intended to be used directly from your code. + /// + /// The to extend. + /// The implementation. + /// The configured options. + /// The delegate that merges an instance of into the pipeline. + public static async Task InvokeThrottlerSentinelAsync(this IDecorator decorator, IThrottlingCache tc, ThrottlingSentinelOptions options, Action transformer) + { + var utcNow = DateTime.UtcNow; + var throttlingContext = options.ContextResolver?.Invoke(decorator.Inner); + if (!string.IsNullOrWhiteSpace(throttlingContext)) + { + ThrottleRequest tr = null; + try + { + await ThrottleLocker.WaitAsync().ConfigureAwait(false); + + if (!tc.TryGetValue(throttlingContext, out tr)) + { + tr = new ThrottleRequest(options.Quota); + Decorator.Enclose(tc).TryAdd(throttlingContext, tr); + } + else + { + tr.Refresh(); + tr.IncrementTotal(); + } + + var window = new TimeRange(utcNow, tr.Expires); + var delta = window.Duration; + var reset = utcNow.Add(delta); + Decorator.Enclose(decorator.Inner.Response.Headers).AddOrUpdate(options.RateLimitHeaderName, tr.Quota.RateLimit.ToString(CultureInfo.InvariantCulture)); + Decorator.Enclose(decorator.Inner.Response.Headers).AddOrUpdate(options.RateLimitRemainingHeaderName, Math.Max(tr.Quota.RateLimit - tr.Total, 0).ToString(CultureInfo.InvariantCulture)); + Decorator.Enclose(decorator.Inner.Response.Headers).AddOrUpdate(options.RateLimitResetHeaderName, Decorator.Enclose(reset).ToUnixEpochTime().ToString(CultureInfo.InvariantCulture)); + if (tr.Total > tr.Quota.RateLimit && tr.Expires > utcNow) + { + var message = options.ResponseBroker?.Invoke(delta, reset); + if (message != null) + { + transformer?.Invoke(message, decorator.Inner.Response); + throw new ThrottlingException((int)message.StatusCode, await message.Content.ReadAsStringAsync().ConfigureAwait(false), tr.Quota.RateLimit, delta, reset); + } + } + } + finally + { + tc[throttlingContext] = tr; + ThrottleLocker.Release(); + } + } + } + + /// + /// Common user agent logic for ASP.NET Core and ASP.NET Core MVC. Not intended to be used directly from your code. + /// + /// The to extend. + /// The configured options. + /// The delegate that merges an instance of into the pipeline. + public static async Task InvokeUserAgentSentinelAsync(this IDecorator decorator, UserAgentSentinelOptions options, Action transformer) + { + var userAgent = decorator.Inner.Request.Headers[HeaderNames.UserAgent].FirstOrDefault(); + if (options.RequireUserAgentHeader) + { + var message = options.ResponseBroker?.Invoke(userAgent); + if (message != null) + { + transformer?.Invoke(message, decorator.Inner.Response); + throw new UserAgentException((int)message.StatusCode, await message.Content.ReadAsStringAsync().ConfigureAwait(false)); + } + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs b/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs index 411001827..69314ad9b 100644 --- a/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs +++ b/src/Cuemon.AspNetCore/Http/Headers/UserAgentSentinelMiddleware.cs @@ -1,6 +1,5 @@ using System; using System.Threading.Tasks; -using Cuemon.AspNetCore.Infrastructure; using Cuemon.IO; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; @@ -37,7 +36,7 @@ public UserAgentSentinelMiddleware(RequestDelegate next, ActionA task that represents the execution of this middleware. public override async Task InvokeAsync(HttpContext context) { - await AspNetCoreInfrastructure.InvokeUserAgentSentinelAsync(context, Options, async (message, response) => + await Decorator.Enclose(context).InvokeUserAgentSentinelAsync(Options, async (message, response) => { context.Response.OnStarting(() => { diff --git a/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs b/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs index 6032178fd..88faf2d17 100644 --- a/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs +++ b/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelMiddleware.cs @@ -1,7 +1,6 @@ using System; using System.Threading.Tasks; using Cuemon.AspNetCore.Http.Headers; -using Cuemon.AspNetCore.Infrastructure; using Cuemon.IO; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; @@ -39,21 +38,13 @@ public ThrottlingSentinelMiddleware(RequestDelegate next, ActionA task that represents the execution of this middleware. public override async Task InvokeAsync(HttpContext context, IThrottlingCache di) { - var exception = false; - try + await Decorator.Enclose(context).InvokeThrottlerSentinelAsync(di, Options, async (message, response) => { - await AspNetCoreInfrastructure.InvokeThrottlerSentinelAsync(context, di, Options, async (message, response) => - { - response.StatusCode = (int)message.StatusCode; - Decorator.Enclose(response.Headers).AddOrUpdateHeaders(message.Headers); - await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); - }).ConfigureAwait(false); - } - catch (ThrottlingException) - { - exception = true; - } - if (!exception) { await Next(context).ConfigureAwait(false); } + response.StatusCode = (int)message.StatusCode; + Decorator.Enclose(response.Headers).AddOrUpdateHeaders(message.Headers); + await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); + }).ConfigureAwait(false); + await Next(context).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs b/src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs deleted file mode 100644 index 83b41f522..000000000 --- a/src/Cuemon.AspNetCore/Infrastructure/AspNetCoreInfrastructure.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Globalization; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.AspNetCore.Http.Headers; -using Cuemon.AspNetCore.Http.Throttling; -using Cuemon.Collections.Generic; -using Microsoft.AspNetCore.Http; -using Microsoft.Net.Http.Headers; - -namespace Cuemon.AspNetCore.Infrastructure -{ - internal static class AspNetCoreInfrastructure - { - private static readonly SemaphoreSlim ThrottleLocker = new SemaphoreSlim(1); - - public static async Task InvokeUserAgentSentinelAsync(HttpContext context, UserAgentSentinelOptions options, Action transformer) - { - var userAgent = context.Request.Headers[HeaderNames.UserAgent].FirstOrDefault(); - if (options.RequireUserAgentHeader) - { - var message = options.ResponseBroker?.Invoke(userAgent); - if (message != null) - { - transformer?.Invoke(message, context.Response); - throw new UserAgentException((int)message.StatusCode, await message.Content.ReadAsStringAsync().ConfigureAwait(false)); - } - } - } - - public static async Task InvokeThrottlerSentinelAsync(HttpContext context, IThrottlingCache tc, ThrottlingSentinelOptions options, Action transformer) - { - var utcNow = DateTime.UtcNow; - var throttlingContext = options.ContextResolver?.Invoke(context); - if (!string.IsNullOrWhiteSpace(throttlingContext)) - { - try - { - await ThrottleLocker.WaitAsync().ConfigureAwait(false); - - if (!tc.TryGetValue(throttlingContext, out var tr)) - { - tr = new ThrottleRequest(options.Quota); - Decorator.Enclose(tc).TryAdd(throttlingContext, tr); - } - else - { - tr.Refresh(); - tr.IncrementTotal(); - } - - var window = new TimeRange(utcNow, tr.Expires); - var delta = window.Duration; - var reset = utcNow.Add(delta); - Decorator.Enclose(context.Response.Headers).AddOrUpdate(options.RateLimitHeaderName, tr.Quota.RateLimit.ToString(CultureInfo.InvariantCulture)); - Decorator.Enclose(context.Response.Headers).AddOrUpdate(options.RateLimitRemainingHeaderName, Math.Max(tr.Quota.RateLimit - tr.Total, 0).ToString(CultureInfo.InvariantCulture)); - Decorator.Enclose(context.Response.Headers).AddOrUpdate(options.RateLimitResetHeaderName, Decorator.Enclose(reset).ToUnixEpochTime().ToString(CultureInfo.InvariantCulture)); - if (tr.Total > tr.Quota.RateLimit && tr.Expires > utcNow) - { - var message = options.ResponseBroker?.Invoke(delta, reset); - if (message != null) - { - context.Response.OnStarting(() => - { - transformer?.Invoke(message, context.Response); - return Task.CompletedTask; - }); - throw new ThrottlingException((int)message.StatusCode, await message.Content.ReadAsStringAsync().ConfigureAwait(false), tr.Quota.RateLimit, delta, reset); - } - } - - tc[throttlingContext] = tr; - } - finally - { - ThrottleLocker.Release(); - } - } - } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Properties/AssemblyInfo.cs b/src/Cuemon.AspNetCore/Properties/AssemblyInfo.cs index af1492a49..661fc9125 100644 --- a/src/Cuemon.AspNetCore/Properties/AssemblyInfo.cs +++ b/src/Cuemon.AspNetCore/Properties/AssemblyInfo.cs @@ -1,6 +1,4 @@ -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; [assembly: ComVisible(false)] -[assembly: Guid("31f1ce52-3e05-495f-8359-dd708905d7d6")] -[assembly: InternalsVisibleTo("Cuemon.AspNetCore.Mvc, PublicKey=00240000048000009400000006020000002400005253413100040000010001002F66D8473F676F4E7B47400527D33951A774422DFFC3DF6D7F87C82E5694E9F3AA626D36BEBEA428AD5B800EFCF6CE87B73268F5A0125A7D38739D344703A1C48785AC1A45B1C27EDFDF2EB30BA2B3E3CEA92E5981C30F3A95685A680B7EBEE66F422D176CD1623019D5A05770B9BA498144B1134593BEA6F674F334CF2B90B0")] \ No newline at end of file +[assembly: Guid("31f1ce52-3e05-495f-8359-dd708905d7d6")] \ No newline at end of file From c442de1527eb669f423eca2c037ce2702ff1170d Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 13 Oct 2020 01:41:54 +0200 Subject: [PATCH 297/385] Tweaked ASP.NET Core test project. --- .../AspNetCoreHostFixture.cs | 2 -- .../Http/Features/FakeHttpResponseFeature.cs | 1 + .../MiddlewareAspNetCoreHostTest.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs index 8cd87adbd..57bd5da5a 100644 --- a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs @@ -6,7 +6,6 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore @@ -61,7 +60,6 @@ public override void ConfigureHost(Test hostTest) Configuration = context.Configuration; HostingEnvironment = context.HostingEnvironment; ConfigureServicesCallback(services); - ServiceProvider = services.BuildServiceProvider(); }) .Configure(app => { diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseFeature.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseFeature.cs index ca2641763..807afa1a2 100644 --- a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseFeature.cs +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseFeature.cs @@ -48,6 +48,7 @@ public override void OnStarting(Func callback, object state) public Task TriggerOnStarting() { _hasStarted = true; + StatusCode = 200; return HasOnStartingCallback ? _callback(_state) : Task.CompletedTask; } } diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareAspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareAspNetCoreHostTest.cs index 9736e3487..87d97e148 100644 --- a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareAspNetCoreHostTest.cs +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareAspNetCoreHostTest.cs @@ -20,7 +20,7 @@ internal MiddlewareAspNetCoreHostTest(Action pipelineConfig hostFixture.ConfigureHost(this); } Host = hostFixture.Host; - ServiceProvider = hostFixture.ServiceProvider; + ServiceProvider = hostFixture.Host.Services; Application = hostFixture.Application; } From 7a32997261249bf66dc7d013a73835fe95b747c1 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 13 Oct 2020 01:43:05 +0200 Subject: [PATCH 298/385] Fixed wording. --- .../Builder/ApplicationBuilderExtensions.cs | 12 +- .../Http/Throttling/MemoryThrottlingCache.cs | 2 - .../ThrottlingSentinelMiddlewareTest.cs | 109 ++++++++++++++++++ 3 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs diff --git a/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs index 37548b8e4..38e001f1b 100644 --- a/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs @@ -16,7 +16,7 @@ public static class ApplicationBuilderExtensions /// Adds a hosting environment HTTP header to the request execution pipeline. /// /// The type that provides the mechanisms to configure an application’s request pipeline. - /// The middleware which need to be configured. + /// The middleware which may be configured. /// A reference to this instance after the operation has completed. /// Default HTTP header name is X-Hosting-Environment. public static IApplicationBuilder UseHostingEnvironment(this IApplicationBuilder builder, Action setup = null) @@ -28,7 +28,7 @@ public static IApplicationBuilder UseHostingEnvironment(this IApplicationBuilder /// Adds a correlation identifier HTTP header to the request execution pipeline. /// /// The type that provides the mechanisms to configure an application’s request pipeline. - /// The middleware which need to be configured. + /// The middleware which may be configured. /// A reference to this instance after the operation has completed. /// Default HTTP header name is X-Correlation-ID. public static IApplicationBuilder UseCorrelationIdentifier(this IApplicationBuilder builder, Action setup = null) @@ -40,7 +40,7 @@ public static IApplicationBuilder UseCorrelationIdentifier(this IApplicationBuil /// Adds a request identifier HTTP header to the request execution pipeline. /// /// The type that provides the mechanisms to configure an application’s request pipeline. - /// The middleware which need to be configured. + /// The middleware which may be configured. /// A reference to this instance after the operation has completed. /// Default HTTP header name is X-Request-ID. public static IApplicationBuilder UseRequestIdentifier(this IApplicationBuilder builder, Action setup = null) @@ -52,7 +52,7 @@ public static IApplicationBuilder UseRequestIdentifier(this IApplicationBuilder /// Adds a HTTP User-Agent header parser to the request execution pipeline. /// /// The type that provides the mechanisms to configure an application’s request pipeline. - /// The middleware which need to be configured. + /// The middleware which may be configured. /// A reference to this instance after the operation has completed. public static IApplicationBuilder UseUserAgentSentinel(this IApplicationBuilder builder, Action setup = null) { @@ -63,9 +63,9 @@ public static IApplicationBuilder UseUserAgentSentinel(this IApplicationBuilder /// Adds a HTTP requests rate limiting / throttling guard to the request execution pipeline. /// /// The type that provides the mechanisms to configure an application’s request pipeline. - /// The middleware which need to be configured. + /// The middleware which may be configured. /// A reference to this instance after the operation has completed. - public static IApplicationBuilder UseThrottlingSentinel(this IApplicationBuilder builder, Action setup) + public static IApplicationBuilder UseThrottlingSentinel(this IApplicationBuilder builder, Action setup = null) { return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); } diff --git a/src/Cuemon.Extensions.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs b/src/Cuemon.Extensions.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs index 2d2bd6036..694fce561 100644 --- a/src/Cuemon.Extensions.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs +++ b/src/Cuemon.Extensions.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs @@ -5,8 +5,6 @@ namespace Cuemon.Extensions.AspNetCore.Http.Throttling { /// /// Provides a simple in-memory representation of the . This class cannot be inherited. - /// Implements the . - /// Implements the . /// /// /// diff --git a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs new file mode 100644 index 000000000..e12a42c36 --- /dev/null +++ b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs @@ -0,0 +1,109 @@ +using System; +using System.Threading.Tasks; +using Cuemon.Extensions.AspNetCore.Builder; +using Cuemon.Extensions.AspNetCore.Http.Throttling; +using Cuemon.Extensions.IO; +using Cuemon.Extensions.Xunit; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Http.Throttling +{ + public class ThrottlingSentinelMiddlewareTest : Test + { + public ThrottlingSentinelMiddlewareTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task InvokeAsync_ShouldThrowThrottlingException_TooManyRequests() + { + var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseThrottlingSentinel(); + app.UseFakeHttpResponseTrigger(); + }, services => + { + services.Configure(o => + { + o.Quota = new ThrottleQuota(10, TimeSpan.FromMinutes(5)); + o.ContextResolver = cr => nameof(ThrottlingSentinelMiddlewareTest); + }); + services.AddSingleton(); + services.AddMemoryThrottlingCache(); + }); + + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var cache = middleware.ServiceProvider.GetRequiredService(); + var pipeline = middleware.Application.Build(); + + var te = await Assert.ThrowsAsync(async () => + { + for (var i = 0; i < 15; i++) + { + await pipeline(context); + } + }); + + var ce = cache[nameof(ThrottlingSentinelMiddlewareTest)]; + Assert.InRange(ce.Total, te.RateLimit, 15); + + Assert.Equal(te.RateLimit, options.Value.Quota.RateLimit); + Assert.Equal(te.Message, options.Value.TooManyRequestsMessage); + Assert.Equal(te.StatusCode, StatusCodes.Status429TooManyRequests); + + Assert.True(options.Value.UseRetryAfterHeader); + Assert.Equal(StatusCodes.Status429TooManyRequests, context.Response.StatusCode); + Assert.Equal(options.Value.TooManyRequestsMessage, context.Response.Body.ToEncodedString()); + } + + [Fact] + public async Task InvokeAsync_ShouldRehydrate() + { + var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseThrottlingSentinel(); + app.UseFakeHttpResponseTrigger(); + }, services => + { + services.Configure(o => + { + o.Quota = new ThrottleQuota(10, TimeSpan.FromSeconds(5)); + o.ContextResolver = cr => nameof(ThrottlingSentinelMiddlewareTest); + }); + services.AddSingleton(); + services.AddMemoryThrottlingCache(); + }); + + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var cache = middleware.ServiceProvider.GetRequiredService(); + var pipeline = middleware.Application.Build(); + + for (var i = 0; i < 10; i++) + { + await pipeline(context); + } + + var te = await Assert.ThrowsAsync(async () => await pipeline(context)); + + TestOutput.WriteLine(te.Delta.ToString()); + + await Task.Delay(te.Delta); + + await pipeline(context); + + var ce = cache[nameof(ThrottlingSentinelMiddlewareTest)]; + + + Assert.True(options.Value.UseRetryAfterHeader); + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } + } +} \ No newline at end of file From e788e68acca12028e0c5b0d15fecc8ecdb54cb21 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 13 Oct 2020 01:47:06 +0200 Subject: [PATCH 299/385] Updated package description. --- src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj b/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj index 3ff8b0d5f..2d8605d5c 100644 --- a/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj +++ b/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj @@ -8,8 +8,8 @@ Cuemon.Data.Integrity Cuemon.Data.Integrity - The Cuemon.Data.Integrity namespace contains classes that provide functionality to help insure integrity of data-centric operations. - cache-validator checksum data-integrity + The Cuemon.Data.Integrity namespace contains types that provide ways for developers to determine and maintain integrity of data that is normally associated with an entity/resource. + cache-validator checksum-builder entity-info data-integrity From c7244bea0884162c2d0c7b300bf2bd9db8568739 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 13 Oct 2020 02:56:33 +0200 Subject: [PATCH 300/385] Use host.Services for setting ServiceProvider. Update pipelines. --- azure-pipelines.yml | 13 ++++++++++++- .../AspNetCoreHostFixture.cs | 4 +++- .../AspNetCoreHostTest.cs | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 315d2bc4e..cbe3ee082 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -103,6 +103,15 @@ jobs: sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/**/*opencover.xml sonar.cs.vstest.reportsPaths=$(Agent.TempDirectory)/*.trx + - task: DotNetCoreCLI@2 + displayName: 'Build netcoreapp3.1 compatible Assemblies' + inputs: + command: 'build' + projects: | + src/**/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj + arguments: '--configuration $(BuildConfiguration) --no-restore --framework netcoreapp3.1' + workingDirectory: '$(BuildSource)' + - task: DotNetCoreCLI@2 displayName: 'Build netcoreapp3.0 compatible Assemblies' inputs: @@ -129,7 +138,9 @@ jobs: displayName: 'Build netstandard2.0 compatible Assemblies' inputs: command: 'build' - projects: src/**/*.csproj + projects: | + src/**/*.csproj + !src/**/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj arguments: '--configuration $(BuildConfiguration) --no-restore --framework netstandard2.0' workingDirectory: '$(BuildSource)' diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs index 57bd5da5a..acea653df 100644 --- a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs @@ -71,7 +71,9 @@ public override void ConfigureHost(Test hostTest) var host = server.Host; host.Start(); - + + ServiceProvider = host.Services; + Host = host; } diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostTest.cs index a92e2ef1f..14f33c9c6 100644 --- a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostTest.cs +++ b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostTest.cs @@ -38,7 +38,7 @@ protected override void InitializeHostFixture(T hostFixture) hostFixture.ConfigureHost(this); } Host = hostFixture.Host; - ServiceProvider = hostFixture.ServiceProvider; + ServiceProvider = hostFixture.Host.Services; Application = hostFixture.Application; } From 2dd81d9b7c25b0fb599125a911cf76fd85fdfc8f Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 13 Oct 2020 03:25:07 +0200 Subject: [PATCH 301/385] Update azure-pipelines.yml for Azure Pipelines Annoying way of defining builds because of one non-2.0 comp. project. --- azure-pipelines.yml | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index cbe3ee082..0ad62dd6f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -139,8 +139,44 @@ jobs: inputs: command: 'build' projects: | - src/**/*.csproj - !src/**/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj + src/**/Cuemon.AspNetCore.csproj + src/**/Cuemon.AspNetCore.Authentication.csproj + src/**/Cuemon.AspNetCore.Mvc.csproj + src/**/Cuemon.AspNetCore.Razor.csproj + src/**/Cuemon.Core.csproj + src/**/Cuemon.Data.csproj + src/**/Cuemon.Data.Integrity.csproj + src/**/Cuemon.Data.SqlClient.csproj + src/**/Cuemon.Diagnostics.csproj + src/**/Cuemon.Extensions.AspNetCore.csproj + src/**/Cuemon.Extensions.AspNetCore.Mvc.csproj + src/**/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj + src/**/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj + src/**/Cuemon.Extensions.Collections.Generic.csproj + src/**/Cuemon.Extensions.Collections.Specialized.csproj + src/**/Cuemon.Extensions.Core.csproj + src/**/Cuemon.Extensions.Data.csproj + src/**/Cuemon.Extensions.Data.Integrity.csproj + src/**/Cuemon.Extensions.DependencyInjection.csproj + src/**/Cuemon.Extensions.Diagnostics.csproj + src/**/Cuemon.Extensions.Hosting.csproj + src/**/Cuemon.Extensions.IO.csproj + src/**/Cuemon.Extensions.Net.csproj + src/**/Cuemon.Extensions.Newtonsoft.Json.csproj + src/**/Cuemon.Extensions.Reflection.csproj + src/**/Cuemon.Extensions.Runtime.Caching.csproj + src/**/Cuemon.Extensions.Text.csproj + src/**/Cuemon.Extensions.Threading.csproj + src/**/Cuemon.Extensions.Xml.csproj + src/**/Cuemon.Extensions.Xunit.csproj + src/**/Cuemon.Extensions.Xunit.Hosting.csproj + src/**/Cuemon.IO.csproj + src/**/Cuemon.Net.csproj + src/**/Cuemon.Resilience.csproj + src/**/Cuemon.Runtime.Caching.csproj + src/**/Cuemon.Security.Cryptography.csproj + src/**/Cuemon.Threading.csproj + src/**/Cuemon.Xml.csproj arguments: '--configuration $(BuildConfiguration) --no-restore --framework netstandard2.0' workingDirectory: '$(BuildSource)' From f96c10735b833e05a04ccf7158da6fde8ddc0e2b Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 13 Oct 2020 15:05:16 +0200 Subject: [PATCH 302/385] Renamed folder to match project name (Cuemon.Extensions.Xunit.Hosting.AspNetCore). --- Cuemon.sln | 2 +- ...Extensions.Xunit.AspNetCore.Hosting.csproj | 26 ------------------- .../ApplicationBuilderExtensions.cs | 0 .../AspNetCoreHostFixture.cs | 0 .../AspNetCoreHostFixtureExtensions.cs | 0 .../AspNetCoreHostTest.cs | 0 ...Extensions.Xunit.Hosting.AspNetCore.csproj | 0 .../Http/FakeHttpContextAccessor.cs | 0 .../Http/Features/FakeHttpResponseFeature.cs | 0 .../Features/FakeHttpResponseMiddleware.cs | 0 .../IAspNetCoreHostFixture.cs | 0 .../IMiddlewareTest.cs | 0 .../IPipelineTest.cs | 0 .../MiddlewareAspNetCoreHostTest.cs | 0 .../MiddlewareTestFactory.cs | 0 15 files changed, 1 insertion(+), 27 deletions(-) delete mode 100644 src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon - Backup.Extensions.Xunit.AspNetCore.Hosting.csproj rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/ApplicationBuilderExtensions.cs (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/AspNetCoreHostFixture.cs (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/AspNetCoreHostFixtureExtensions.cs (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/AspNetCoreHostTest.cs (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/Http/FakeHttpContextAccessor.cs (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/Http/Features/FakeHttpResponseFeature.cs (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/Http/Features/FakeHttpResponseMiddleware.cs (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/IAspNetCoreHostFixture.cs (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/IMiddlewareTest.cs (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/IPipelineTest.cs (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/MiddlewareAspNetCoreHostTest.cs (100%) rename src/{Cuemon.Extensions.Xunit.AspNetCore.Hosting => Cuemon.Extensions.Xunit.Hosting.AspNetCore}/MiddlewareTestFactory.cs (100%) diff --git a/Cuemon.sln b/Cuemon.sln index 46e99f9c4..677d76a33 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -127,7 +127,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Newtonsof EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Data.SqlClient.Tests", "test\Cuemon.Data.SqlClient.Tests\Cuemon.Data.SqlClient.Tests.csproj", "{A9610C9E-1944-4771-A5E1-CE47ADA243D5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore", "src\Cuemon.Extensions.Xunit.AspNetCore.Hosting\Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj", "{200BDF91-E7C7-4CB4-A39D-E1A5374C5602}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore", "src\Cuemon.Extensions.Xunit.Hosting.AspNetCore\Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj", "{200BDF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon - Backup.Extensions.Xunit.AspNetCore.Hosting.csproj b/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon - Backup.Extensions.Xunit.AspNetCore.Hosting.csproj deleted file mode 100644 index 829bd9d2a..000000000 --- a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon - Backup.Extensions.Xunit.AspNetCore.Hosting.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - netcoreapp3.1 - 200bdf91-e7c7-4cb4-a39d-e1a5374c5602 - - - - Cuemon.Extensions.Xunit.AspNetCore.Hosting - Cuemon.Extensions.Xunit.AspNetCore.Hosting - The Cuemon.Extensions.Xunit.AspNetCore.Hosting namespace contains types that provides a uniform way of doing unit testing used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. - host-test class-fixture host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ApplicationBuilderExtensions.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/ApplicationBuilderExtensions.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ApplicationBuilderExtensions.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixture.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixtureExtensions.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixtureExtensions.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostFixtureExtensions.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixtureExtensions.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostTest.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/AspNetCoreHostTest.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostTest.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/FakeHttpContextAccessor.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/FakeHttpContextAccessor.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseFeature.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseFeature.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseMiddleware.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseMiddleware.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/Http/Features/FakeHttpResponseMiddleware.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseMiddleware.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IAspNetCoreHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IAspNetCoreHostFixture.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IAspNetCoreHostFixture.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IAspNetCoreHostFixture.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IMiddlewareTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IMiddlewareTest.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IMiddlewareTest.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IMiddlewareTest.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IPipelineTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IPipelineTest.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/IPipelineTest.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IPipelineTest.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareAspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareAspNetCoreHostTest.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareAspNetCoreHostTest.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareAspNetCoreHostTest.cs diff --git a/src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareTestFactory.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareTestFactory.cs similarity index 100% rename from src/Cuemon.Extensions.Xunit.AspNetCore.Hosting/MiddlewareTestFactory.cs rename to src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareTestFactory.cs From 044e662478acf9fa55b7bd890a35c82389c95ba3 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 13 Oct 2020 15:34:30 +0200 Subject: [PATCH 303/385] Updated reference. --- .../Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj | 2 +- .../Http/Throttling/ThrottlingSentinelMiddlewareTest.cs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj b/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj index fe70d327a..c1adcefe4 100644 --- a/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj +++ b/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs index e12a42c36..58c88ebe5 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs @@ -66,6 +66,7 @@ public async Task InvokeAsync_ShouldThrowThrottlingException_TooManyRequests() [Fact] public async Task InvokeAsync_ShouldRehydrate() { + var window = TimeSpan.FromSeconds(5); var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => { app.UseThrottlingSentinel(); @@ -74,7 +75,7 @@ public async Task InvokeAsync_ShouldRehydrate() { services.Configure(o => { - o.Quota = new ThrottleQuota(10, TimeSpan.FromSeconds(5)); + o.Quota = new ThrottleQuota(10, window); o.ContextResolver = cr => nameof(ThrottlingSentinelMiddlewareTest); }); services.AddSingleton(); @@ -95,13 +96,13 @@ public async Task InvokeAsync_ShouldRehydrate() TestOutput.WriteLine(te.Delta.ToString()); - await Task.Delay(te.Delta); + await Task.Delay(window); await pipeline(context); var ce = cache[nameof(ThrottlingSentinelMiddlewareTest)]; - + Assert.True(window >= te.Delta, "window >= te.Delta"); Assert.True(options.Value.UseRetryAfterHeader); Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); } From 81aa309210c86ba4305f5992eeda6d7a93ac9f57 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 13 Oct 2020 15:42:46 +0200 Subject: [PATCH 304/385] Aligned folder structure with project name. --- Cuemon.sln | 2 +- .../Assets/Correlation.cs | 0 .../Assets/ScopedCorrelation.cs | 0 .../Assets/SingletonCorrelation.cs | 0 .../Assets/TransientCorrelation.cs | 0 .../Cuemon.Extensions.Xunit.Hosting.Tests.csproj} | 0 .../HostTestTest.cs | 0 .../appsettings.json | 0 8 files changed, 1 insertion(+), 1 deletion(-) rename test/{Cuemon.Extensions.Hosting.Xunit.Tests => Cuemon.Extensions.Xunit.Hosting.Tests}/Assets/Correlation.cs (100%) rename test/{Cuemon.Extensions.Hosting.Xunit.Tests => Cuemon.Extensions.Xunit.Hosting.Tests}/Assets/ScopedCorrelation.cs (100%) rename test/{Cuemon.Extensions.Hosting.Xunit.Tests => Cuemon.Extensions.Xunit.Hosting.Tests}/Assets/SingletonCorrelation.cs (100%) rename test/{Cuemon.Extensions.Hosting.Xunit.Tests => Cuemon.Extensions.Xunit.Hosting.Tests}/Assets/TransientCorrelation.cs (100%) rename test/{Cuemon.Extensions.Hosting.Xunit.Tests/Cuemon.Extensions.Hosting.Xunit.Tests.csproj => Cuemon.Extensions.Xunit.Hosting.Tests/Cuemon.Extensions.Xunit.Hosting.Tests.csproj} (100%) rename test/{Cuemon.Extensions.Hosting.Xunit.Tests => Cuemon.Extensions.Xunit.Hosting.Tests}/HostTestTest.cs (100%) rename test/{Cuemon.Extensions.Hosting.Xunit.Tests => Cuemon.Extensions.Xunit.Hosting.Tests}/appsettings.json (100%) diff --git a/Cuemon.sln b/Cuemon.sln index 677d76a33..c124fa66a 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -115,7 +115,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Hosting", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Tests", "test\Cuemon.Extensions.Xunit.Tests\Cuemon.Extensions.Xunit.Tests.csproj", "{2108E7E7-F002-481C-B17F-918E76D98378}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Hosting.Xunit.Tests", "test\Cuemon.Extensions.Hosting.Xunit.Tests\Cuemon.Extensions.Hosting.Xunit.Tests.csproj", "{86B43822-0733-416E-8DA2-666C5657974F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Hosting.Tests", "test\Cuemon.Extensions.Xunit.Hosting.Tests\Cuemon.Extensions.Xunit.Hosting.Tests.csproj", "{86B43822-0733-416E-8DA2-666C5657974F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Runtime.Caching.Tests", "test\Cuemon.Runtime.Caching.Tests\Cuemon.Runtime.Caching.Tests.csproj", "{581174AB-62AA-4A04-85DE-4F9E307C9712}" EndProject diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/Correlation.cs b/test/Cuemon.Extensions.Xunit.Hosting.Tests/Assets/Correlation.cs similarity index 100% rename from test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/Correlation.cs rename to test/Cuemon.Extensions.Xunit.Hosting.Tests/Assets/Correlation.cs diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/ScopedCorrelation.cs b/test/Cuemon.Extensions.Xunit.Hosting.Tests/Assets/ScopedCorrelation.cs similarity index 100% rename from test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/ScopedCorrelation.cs rename to test/Cuemon.Extensions.Xunit.Hosting.Tests/Assets/ScopedCorrelation.cs diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/SingletonCorrelation.cs b/test/Cuemon.Extensions.Xunit.Hosting.Tests/Assets/SingletonCorrelation.cs similarity index 100% rename from test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/SingletonCorrelation.cs rename to test/Cuemon.Extensions.Xunit.Hosting.Tests/Assets/SingletonCorrelation.cs diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/TransientCorrelation.cs b/test/Cuemon.Extensions.Xunit.Hosting.Tests/Assets/TransientCorrelation.cs similarity index 100% rename from test/Cuemon.Extensions.Hosting.Xunit.Tests/Assets/TransientCorrelation.cs rename to test/Cuemon.Extensions.Xunit.Hosting.Tests/Assets/TransientCorrelation.cs diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/Cuemon.Extensions.Hosting.Xunit.Tests.csproj b/test/Cuemon.Extensions.Xunit.Hosting.Tests/Cuemon.Extensions.Xunit.Hosting.Tests.csproj similarity index 100% rename from test/Cuemon.Extensions.Hosting.Xunit.Tests/Cuemon.Extensions.Hosting.Xunit.Tests.csproj rename to test/Cuemon.Extensions.Xunit.Hosting.Tests/Cuemon.Extensions.Xunit.Hosting.Tests.csproj diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/HostTestTest.cs b/test/Cuemon.Extensions.Xunit.Hosting.Tests/HostTestTest.cs similarity index 100% rename from test/Cuemon.Extensions.Hosting.Xunit.Tests/HostTestTest.cs rename to test/Cuemon.Extensions.Xunit.Hosting.Tests/HostTestTest.cs diff --git a/test/Cuemon.Extensions.Hosting.Xunit.Tests/appsettings.json b/test/Cuemon.Extensions.Xunit.Hosting.Tests/appsettings.json similarity index 100% rename from test/Cuemon.Extensions.Hosting.Xunit.Tests/appsettings.json rename to test/Cuemon.Extensions.Xunit.Hosting.Tests/appsettings.json From 4f7b683068596885f49138e38051151abde2d4f1 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 13 Oct 2020 21:43:03 +0200 Subject: [PATCH 305/385] Simplified and more uniform design. --- .../AspNetCoreHostFixture.cs | 77 +++++----- .../AspNetCoreHostTest.cs | 14 +- ...Extensions.Xunit.Hosting.AspNetCore.csproj | 4 +- .../IAspNetCoreHostFixture.cs | 8 -- .../IMiddlewareTest.cs | 7 +- .../MiddlewareAspNetCoreHostTest.cs | 9 ++ .../HostFixture.cs | 25 +++- .../HostTest.cs | 53 +++++-- .../IHostFixture.cs | 16 ++- .../Cuemon.AspNetCore.Tests.csproj | 4 - .../UserAgentSentinelMiddlewareTest.cs | 136 +++++++++--------- .../ThrottlingSentinelMiddlewareTest.cs | 80 +++++------ .../Assets/UserSecretsHostFixture.cs | 10 +- 13 files changed, 244 insertions(+), 199 deletions(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs index acea653df..15091f8c9 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs @@ -1,12 +1,12 @@ using System; using System.IO; -using System.Linq; -using Cuemon.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore { @@ -41,48 +41,45 @@ public override void ConfigureHost(Test hostTest) Validator.ThrowIfNull(hostTest, nameof(hostTest)); Validator.ThrowIfNotContainsType(hostTestType, nameof(hostTestType), $"{nameof(hostTest)} is not assignable from AspNetCoreHostTest.", typeof(AspNetCoreHostTest<>)); - var server = new TestServer(new WebHostBuilder() - .ConfigureAppConfiguration((context, config) => + Host = new HostBuilder() + .ConfigureWebHost(webBuilder => { - config.AddEnvironmentVariables("ASPNETCORE_"); - config.SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", true, true) - .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) - .AddEnvironmentVariables(); - }) - .ConfigureServices((context, services) => - { - var flags = new MemberReflection(excludeStatic: true, excludePublic: true).Flags; - var hostTestTypeBase = Decorator.Enclose(hostTestType).GetInheritedTypes().Single(t => t.BaseType == typeof(Test)); - hostTestTypeBase.GetField("_configuration", flags).SetValue(hostTest, context.Configuration); - hostTestTypeBase.GetField("_hostingEnvironment", flags).SetValue(hostTest, context.HostingEnvironment); - - Configuration = context.Configuration; - HostingEnvironment = context.HostingEnvironment; - ConfigureServicesCallback(services); - }) - .Configure(app => - { - ConfigureApplicationCallback(app); - Application = app; - } - )); - - var host = server.Host; - - host.Start(); + webBuilder + .UseTestServer() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseEnvironment("Development") + .ConfigureAppConfiguration((context, config) => + { + config.AddEnvironmentVariables("ASPNETCORE_"); + config.AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) + .AddEnvironmentVariables(); - ServiceProvider = host.Services; - - Host = host; + ConfigureCallback(config.Build(), context.HostingEnvironment); + }) + .ConfigureLogging((context, logging) => + { + logging.AddConfiguration(context.Configuration.GetSection("Logging")); + logging.AddConsole(); + logging.AddDebug(); + logging.AddEventSourceLogger(); + }) + .ConfigureServices((context, services) => + { + Configuration = context.Configuration; + HostingEnvironment = context.HostingEnvironment; + ConfigureServicesCallback(services); + ServiceProvider = services.BuildServiceProvider(); + }) + .Configure(app => + { + ConfigureApplicationCallback(app); + Application = app; + } + ); + }).Start(); } - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - public new IWebHost Host { get; private set; } - /// /// Gets or sets the delegate that configures the HTTP request pipeline. /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostTest.cs index 14f33c9c6..c56da5aba 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostTest.cs @@ -1,6 +1,5 @@ using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -19,9 +18,9 @@ public abstract class AspNetCoreHostTest : HostTest where T : class, IAspN /// /// Initializes a new instance of the class. /// - /// An implementation of the interface. + /// An implementation of the interface. /// An implementation of the interface. - protected AspNetCoreHostTest(T aspNetCoreHostFixture, ITestOutputHelper output = null) : base(aspNetCoreHostFixture, output) + protected AspNetCoreHostTest(T hostFixture, ITestOutputHelper output = null) : base(hostFixture, output) { } @@ -33,6 +32,7 @@ protected override void InitializeHostFixture(T hostFixture) { if (!hostFixture.HasValidState()) { + hostFixture.ConfigureCallback = Configure; hostFixture.ConfigureServicesCallback = ConfigureServices; hostFixture.ConfigureApplicationCallback = ConfigureApplication; hostFixture.ConfigureHost(this); @@ -41,13 +41,7 @@ protected override void InitializeHostFixture(T hostFixture) ServiceProvider = hostFixture.Host.Services; Application = hostFixture.Application; } - - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - public new IWebHost Host { get; protected set; } - + /// /// Gets the initialized by the . /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj index 33632266c..dde37d86c 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj @@ -8,8 +8,8 @@ Cuemon.Extensions.Xunit.Hosting.AspNetCore Cuemon.Extensions.Xunit.Hosting.AspNetCore - The Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. - host-test class-fixture host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services + The Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. + asp-net-core-host-test class-fixture asp-net-core-host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services test-server diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IAspNetCoreHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IAspNetCoreHostFixture.cs index 927e2eceb..17e693a9a 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IAspNetCoreHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IAspNetCoreHostFixture.cs @@ -1,7 +1,5 @@ using System; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore { @@ -16,11 +14,5 @@ public interface IAspNetCoreHostFixture : IHostFixture, IPipelineTest ///
/// The delegate that configures the HTTP request pipeline. Action ConfigureApplicationCallback { get; set; } - - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - public new IWebHost Host { get; } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IMiddlewareTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IMiddlewareTest.cs index 3d00f23a0..c4bb273d8 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IMiddlewareTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IMiddlewareTest.cs @@ -1,11 +1,14 @@ -namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +using System; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore { /// /// Represents the members needed for ASP.NET Core middleware testing. /// /// /// - public interface IMiddlewareTest : IServiceTest, IPipelineTest + /// + public interface IMiddlewareTest : IServiceTest, IPipelineTest, IDisposable { } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareAspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareAspNetCoreHostTest.cs index 87d97e148..f3887bf6f 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareAspNetCoreHostTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareAspNetCoreHostTest.cs @@ -15,6 +15,7 @@ internal MiddlewareAspNetCoreHostTest(Action pipelineConfig _serviceConfigurator = serviceConfigurator; if (!hostFixture.HasValidState()) { + hostFixture.ConfigureCallback = Configure; hostFixture.ConfigureServicesCallback = ConfigureServices; hostFixture.ConfigureApplicationCallback = ConfigureApplication; hostFixture.ConfigureHost(this); @@ -37,5 +38,13 @@ public override void ConfigureServices(IServiceCollection services) { _serviceConfigurator(services); } + + /// + /// Called when this object is being disposed by either or having disposing set to true and is false. + /// + protected override void OnDisposeManagedResources() + { + Host?.Dispose(); + } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs index 40fa7ad81..eae19ee66 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs @@ -1,7 +1,5 @@ using System; using System.IO; -using System.Linq; -using Cuemon.Reflection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -47,14 +45,11 @@ public virtual void ConfigureHost(Test hostTest) .AddJsonFile("appsettings.json", true, true) .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) .AddEnvironmentVariables(); + + ConfigureCallback(config.Build(), context.HostingEnvironment); }) .ConfigureServices((context, services) => { - var flags = new MemberReflection(excludeStatic: true, excludePublic: true).Flags; - var hostTestTypeBase = Decorator.Enclose(hostTestType).GetInheritedTypes().Single(t => t.BaseType == typeof(Test)); - hostTestTypeBase.GetField("_configuration", flags).SetValue(hostTest, context.Configuration); - hostTestTypeBase.GetField("_hostingEnvironment", flags).SetValue(hostTest, context.HostingEnvironment); - Configuration = context.Configuration; HostingEnvironment = context.HostingEnvironment; ConfigureServicesCallback(services); @@ -62,6 +57,22 @@ public virtual void ConfigureHost(Test hostTest) }).Build(); } + #if NETSTANDARD + /// + /// Gets or sets the delegate that initializes the test class. + /// + /// The delegate that initializes the test class. + /// Mimics the Startup convention. + public Action ConfigureCallback { get; set; } + #elif NETCOREAPP + /// + /// Gets or sets the delegate that initializes the test class. + /// + /// The delegate that initializes the test class. + /// Mimics the Startup convention. + public Action ConfigureCallback { get; set; } + #endif + /// /// Gets or sets the delegate that adds services to the container. /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs index bf27d277d..756e10bbe 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostTest.cs @@ -16,13 +16,6 @@ namespace Cuemon.Extensions.Xunit.Hosting /// The class needed to be designed in this rather complex way, as this is the only way that xUnit supports a shared context. The need for shared context is theoretical at best, but it does opt-in for Scoped instances. public abstract class HostTest : Test, IClassFixture where T : class, IHostFixture { - private readonly IConfiguration _configuration; - #if NETSTANDARD - private readonly IHostingEnvironment _hostingEnvironment; - #elif NETCOREAPP - private readonly IHostEnvironment _hostingEnvironment; - #endif - /// /// Initializes a new instance of the class. /// @@ -32,8 +25,6 @@ protected HostTest(T hostFixture, ITestOutputHelper output = null) : base(output { Validator.ThrowIfNull(hostFixture, nameof(hostFixture)); InitializeHostFixture(hostFixture); - if (_configuration == null) { _configuration = hostFixture.Configuration; } - if (_hostingEnvironment == null) { _hostingEnvironment = hostFixture.HostingEnvironment; } } /// @@ -44,11 +35,13 @@ protected virtual void InitializeHostFixture(T hostFixture) { if (!hostFixture.HasValidState()) { + hostFixture.ConfigureCallback = Configure; hostFixture.ConfigureServicesCallback = ConfigureServices; hostFixture.ConfigureHost(this); } Host = hostFixture.Host; ServiceProvider = hostFixture.ServiceProvider; + Configure(hostFixture.Configuration, hostFixture.HostingEnvironment); } /// @@ -67,20 +60,56 @@ protected virtual void InitializeHostFixture(T hostFixture) /// Gets the initialized by the . /// /// The initialized by the . - public IConfiguration Configuration => _configuration; + public IConfiguration Configuration + { + get; + private set; + } #if NETSTANDARD /// /// Gets the initialized by the . /// /// The initialized by the . - public IHostingEnvironment HostingEnvironment => _hostingEnvironment; + public IHostingEnvironment HostingEnvironment + { + get; + private set; + } #elif NETCOREAPP /// /// Gets the initialized by the . /// /// The initialized by the . - public IHostEnvironment HostingEnvironment => _hostingEnvironment; + public IHostEnvironment HostingEnvironment + { + get; + private set; + } + #endif + + #if NETSTANDARD + /// + /// Adds and to this instance. + /// + /// The initialized by the . + /// The initialized by the . + public virtual void Configure(IConfiguration configuration, IHostingEnvironment environment) + { + Configuration = configuration; + HostingEnvironment = environment; + } + #elif NETCOREAPP + /// + /// Adds and to this instance. + /// + /// The initialized by the . + /// The initialized by the . + public virtual void Configure(IConfiguration configuration, IHostEnvironment environment) + { + Configuration = configuration; + HostingEnvironment = environment; + } #endif /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs index e50998db2..61f2370d0 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs @@ -9,8 +9,22 @@ namespace Cuemon.Extensions.Xunit.Hosting /// Provides a way to use Microsoft Dependency Injection in unit tests. /// /// - public interface IHostFixture : IServiceTest, IDisposable + public interface IHostFixture : IServiceTest { + #if NETSTANDARD + /// + /// Gets or sets the delegate that adds configuration and environment information to a . + /// + /// The delegate that adds configuration and environment information to a . + Action ConfigureCallback { get; set; } + #elif NETCOREAPP + /// + /// Gets or sets the delegate that adds configuration and environment information to a . + /// + /// The delegate that adds configuration and environment information to a . + Action ConfigureCallback { get; set; } + #endif + /// /// Gets or sets the delegate that adds services to the container. /// diff --git a/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj b/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj index c1adcefe4..7df076545 100644 --- a/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj +++ b/test/Cuemon.AspNetCore.Tests/Cuemon.AspNetCore.Tests.csproj @@ -11,8 +11,4 @@ - - - - \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs index 37675d684..8d95949bd 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs @@ -23,37 +23,35 @@ public UserAgentSentinelMiddlewareTest(ITestOutputHelper output) : base(output) [Fact] public async Task InvokeAsync_ShouldThrowUserAgentException_BadRequest() { - var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => { app.UseUserAgentSentinel(); app.UseFakeHttpResponseTrigger(); }, services => { - services.Configure(o => - { - o.RequireUserAgentHeader = true; - }); + services.Configure(o => { o.RequireUserAgentHeader = true; }); services.AddScoped(); - }); - - var context = middleware.ServiceProvider.GetRequiredService().HttpContext; - var options = middleware.ServiceProvider.GetRequiredService>(); - var pipeline = middleware.Application.Build(); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); - var uae = await Assert.ThrowsAsync(async () => await pipeline(context)); + var uae = await Assert.ThrowsAsync(async () => await pipeline(context)); - Assert.Equal(uae.Message, options.Value.BadRequestMessage); - Assert.Equal(uae.StatusCode, StatusCodes.Status400BadRequest); + Assert.Equal(uae.Message, options.Value.BadRequestMessage); + Assert.Equal(uae.StatusCode, StatusCodes.Status400BadRequest); - Assert.True(options.Value.RequireUserAgentHeader); - Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode); - Assert.Equal(options.Value.BadRequestMessage, context.Response.Body.ToEncodedString()); + Assert.True(options.Value.RequireUserAgentHeader); + Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode); + Assert.Equal(options.Value.BadRequestMessage, context.Response.Body.ToEncodedString()); + } } [Fact] public async Task InvokeAsync_ShouldThrowUserAgentException_Forbidden() { - var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => { app.UseUserAgentSentinel(); app.UseFakeHttpResponseTrigger(); @@ -66,30 +64,31 @@ public async Task InvokeAsync_ShouldThrowUserAgentException_Forbidden() o.AllowedUserAgents.Add("Cuemon-Agent"); }); services.AddScoped(); - }); - - var context = middleware.ServiceProvider.GetRequiredService().HttpContext; - var options = middleware.ServiceProvider.GetRequiredService>(); - var pipeline = middleware.Application.Build(); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); - context.Request.Headers.Add(HeaderNames.UserAgent, "Invalid-Agent"); + context.Request.Headers.Add(HeaderNames.UserAgent, "Invalid-Agent"); - var uae = await Assert.ThrowsAsync(async () => await pipeline(context)); + var uae = await Assert.ThrowsAsync(async () => await pipeline(context)); - Assert.Equal(uae.Message, options.Value.ForbiddenMessage); - Assert.Equal(uae.StatusCode, StatusCodes.Status403Forbidden); + Assert.Equal(uae.Message, options.Value.ForbiddenMessage); + Assert.Equal(uae.StatusCode, StatusCodes.Status403Forbidden); - Assert.True(options.Value.RequireUserAgentHeader); - Assert.True(options.Value.ValidateUserAgentHeader); - Assert.True(options.Value.AllowedUserAgents.Any()); - Assert.Equal(StatusCodes.Status403Forbidden, context.Response.StatusCode); - Assert.Equal(options.Value.ForbiddenMessage, context.Response.Body.ToEncodedString()); + Assert.True(options.Value.RequireUserAgentHeader); + Assert.True(options.Value.ValidateUserAgentHeader); + Assert.True(options.Value.AllowedUserAgents.Any()); + Assert.Equal(StatusCodes.Status403Forbidden, context.Response.StatusCode); + Assert.Equal(options.Value.ForbiddenMessage, context.Response.Body.ToEncodedString()); + } } [Fact] public async Task InvokeAsync_ShouldThrowUserAgentException_BadRequest_BecauseOfUseGenericResponse() { - var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => { app.UseUserAgentSentinel(); app.UseFakeHttpResponseTrigger(); @@ -103,50 +102,52 @@ public async Task InvokeAsync_ShouldThrowUserAgentException_BadRequest_BecauseOf o.AllowedUserAgents.Add("Cuemon-Agent"); }); services.AddScoped(); - }); - - var context = middleware.ServiceProvider.GetRequiredService().HttpContext; - var options = middleware.ServiceProvider.GetRequiredService>(); - var pipeline = middleware.Application.Build(); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); - context.Request.Headers.Add(HeaderNames.UserAgent, "Invalid-Agent"); + context.Request.Headers.Add(HeaderNames.UserAgent, "Invalid-Agent"); - var uae = await Assert.ThrowsAsync(async () => await pipeline(context)); + var uae = await Assert.ThrowsAsync(async () => await pipeline(context)); - Assert.Equal(uae.Message, options.Value.BadRequestMessage); - Assert.Equal(uae.StatusCode, StatusCodes.Status400BadRequest); + Assert.Equal(uae.Message, options.Value.BadRequestMessage); + Assert.Equal(uae.StatusCode, StatusCodes.Status400BadRequest); - Assert.True(options.Value.RequireUserAgentHeader); - Assert.True(options.Value.ValidateUserAgentHeader); - Assert.True(options.Value.UseGenericResponse); - Assert.True(options.Value.AllowedUserAgents.Any()); - Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode); - Assert.Equal(options.Value.BadRequestMessage, context.Response.Body.ToEncodedString()); + Assert.True(options.Value.RequireUserAgentHeader); + Assert.True(options.Value.ValidateUserAgentHeader); + Assert.True(options.Value.UseGenericResponse); + Assert.True(options.Value.AllowedUserAgents.Any()); + Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode); + Assert.Equal(options.Value.BadRequestMessage, context.Response.Body.ToEncodedString()); + } } [Fact] public async Task InvokeAsync_ShouldAllowRequestUnconditional() { - var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => { app.UseUserAgentSentinel(); app.UseFakeHttpResponseTrigger(); - }); - - var context = middleware.ServiceProvider.GetRequiredService().HttpContext; - var options = middleware.ServiceProvider.GetRequiredService>(); - var pipeline = middleware.Application.Build(); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); - await pipeline(context); + await pipeline(context); - Assert.False(options.Value.RequireUserAgentHeader); - Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + Assert.False(options.Value.RequireUserAgentHeader); + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } } [Fact] public async Task InvokeAsync_ShouldAllowRequestAfterBeingValidated() { - var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => { app.UseUserAgentSentinel(); app.UseFakeHttpResponseTrigger(); @@ -159,19 +160,20 @@ public async Task InvokeAsync_ShouldAllowRequestAfterBeingValidated() o.AllowedUserAgents.Add("Cuemon-Agent"); }); services.AddScoped(); - }); - - var context = middleware.ServiceProvider.GetRequiredService().HttpContext; - var options = middleware.ServiceProvider.GetRequiredService>(); - var pipeline = middleware.Application.Build(); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); - context.Request.Headers.Add(HeaderNames.UserAgent, "Cuemon-Agent"); + context.Request.Headers.Add(HeaderNames.UserAgent, "Cuemon-Agent"); - await pipeline(context); + await pipeline(context); - Assert.True(options.Value.RequireUserAgentHeader); - Assert.True(options.Value.ValidateUserAgentHeader); - Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + Assert.True(options.Value.RequireUserAgentHeader); + Assert.True(options.Value.ValidateUserAgentHeader); + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } } } } \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs index 58c88ebe5..ec4f7ceab 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs @@ -23,7 +23,7 @@ public ThrottlingSentinelMiddlewareTest(ITestOutputHelper output) : base(output) [Fact] public async Task InvokeAsync_ShouldThrowThrottlingException_TooManyRequests() { - var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => { app.UseThrottlingSentinel(); app.UseFakeHttpResponseTrigger(); @@ -36,38 +36,39 @@ public async Task InvokeAsync_ShouldThrowThrottlingException_TooManyRequests() }); services.AddSingleton(); services.AddMemoryThrottlingCache(); - }); - - var context = middleware.ServiceProvider.GetRequiredService().HttpContext; - var options = middleware.ServiceProvider.GetRequiredService>(); - var cache = middleware.ServiceProvider.GetRequiredService(); - var pipeline = middleware.Application.Build(); - - var te = await Assert.ThrowsAsync(async () => + })) { - for (var i = 0; i < 15; i++) + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var cache = middleware.ServiceProvider.GetRequiredService(); + var pipeline = middleware.Application.Build(); + + var te = await Assert.ThrowsAsync(async () => { - await pipeline(context); - } - }); + for (var i = 0; i < 15; i++) + { + await pipeline(context); + } + }); - var ce = cache[nameof(ThrottlingSentinelMiddlewareTest)]; - Assert.InRange(ce.Total, te.RateLimit, 15); + var ce = cache[nameof(ThrottlingSentinelMiddlewareTest)]; + Assert.InRange(ce.Total, te.RateLimit, 15); - Assert.Equal(te.RateLimit, options.Value.Quota.RateLimit); - Assert.Equal(te.Message, options.Value.TooManyRequestsMessage); - Assert.Equal(te.StatusCode, StatusCodes.Status429TooManyRequests); + Assert.Equal(te.RateLimit, options.Value.Quota.RateLimit); + Assert.Equal(te.Message, options.Value.TooManyRequestsMessage); + Assert.Equal(te.StatusCode, StatusCodes.Status429TooManyRequests); - Assert.True(options.Value.UseRetryAfterHeader); - Assert.Equal(StatusCodes.Status429TooManyRequests, context.Response.StatusCode); - Assert.Equal(options.Value.TooManyRequestsMessage, context.Response.Body.ToEncodedString()); + Assert.True(options.Value.UseRetryAfterHeader); + Assert.Equal(StatusCodes.Status429TooManyRequests, context.Response.StatusCode); + Assert.Equal(options.Value.TooManyRequestsMessage, context.Response.Body.ToEncodedString()); + } } [Fact] public async Task InvokeAsync_ShouldRehydrate() { var window = TimeSpan.FromSeconds(5); - var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => { app.UseThrottlingSentinel(); app.UseFakeHttpResponseTrigger(); @@ -80,31 +81,30 @@ public async Task InvokeAsync_ShouldRehydrate() }); services.AddSingleton(); services.AddMemoryThrottlingCache(); - }); - - var context = middleware.ServiceProvider.GetRequiredService().HttpContext; - var options = middleware.ServiceProvider.GetRequiredService>(); - var cache = middleware.ServiceProvider.GetRequiredService(); - var pipeline = middleware.Application.Build(); - - for (var i = 0; i < 10; i++) + })) { - await pipeline(context); - } + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var cache = middleware.ServiceProvider.GetRequiredService(); + var pipeline = middleware.Application.Build(); - var te = await Assert.ThrowsAsync(async () => await pipeline(context)); + for (var i = 0; i < 10; i++) + { + await pipeline(context); + } - TestOutput.WriteLine(te.Delta.ToString()); + var te = await Assert.ThrowsAsync(async () => await pipeline(context)); - await Task.Delay(window); + TestOutput.WriteLine(te.Delta.ToString()); - await pipeline(context); + await Task.Delay(window); - var ce = cache[nameof(ThrottlingSentinelMiddlewareTest)]; + await pipeline(context); - Assert.True(window >= te.Delta, "window >= te.Delta"); - Assert.True(options.Value.UseRetryAfterHeader); - Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + Assert.True(window >= te.Delta, "window >= te.Delta"); + Assert.True(options.Value.UseRetryAfterHeader); + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } } } } \ No newline at end of file diff --git a/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs b/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs index 4fedb3503..22647b1cb 100644 --- a/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs +++ b/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs @@ -1,8 +1,6 @@ using System.IO; -using System.Linq; using Cuemon.Extensions.Xunit; using Cuemon.Extensions.Xunit.Hosting; -using Cuemon.Reflection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -26,13 +24,13 @@ public override void ConfigureHost(Test hostTest) .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) .AddEnvironmentVariables() .AddUserSecrets(); + + ConfigureCallback(config.Build(), context.HostingEnvironment); }) .ConfigureServices((context, services) => { - var flags = new MemberReflection(excludeStatic: true, excludePublic: true).Flags; - var hostTestTypeBase = Decorator.Enclose(hostTestType).GetInheritedTypes().Single(t => t.BaseType == typeof(Test)); - hostTestTypeBase.GetField("_configuration", flags).SetValue(hostTest, context.Configuration); - hostTestTypeBase.GetField("_hostingEnvironment", flags).SetValue(hostTest, context.HostingEnvironment); + Configuration = context.Configuration; + HostingEnvironment = context.HostingEnvironment; ConfigureServicesCallback(services); ServiceProvider = services.BuildServiceProvider(); }).Build(); From 4f78cbc37cd74281e58a971ba3be87eced35069f Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 13 Oct 2020 23:17:49 +0200 Subject: [PATCH 306/385] First stable version of Cuemon.Extensions.Xunit.Hosting.AspNetCore. --- Cuemon.sln | 7 +++ ...mon.Extensions.Xunit.Hosting.AspNetCore.md | 18 ++++++ .../AspNetCoreHostTest.cs | 15 +---- ...Extensions.Xunit.Hosting.AspNetCore.csproj | 2 +- .../HostingEnvironmentMiddlewareTest.cs | 3 +- .../CorrelationIdentifierMiddlewareTest.cs | 10 +++ .../RequestIdentifierMiddlewareTest.cs | 10 +++ .../AspNetCoreHostTestTest.cs | 63 +++++++++++++++++++ .../Assets/BoolMiddleware.cs | 42 +++++++++++++ .../Assets/BoolOptions.cs | 17 +++++ ...ions.Xunit.Hosting.AspNetCore.Tests.csproj | 12 ++++ 11 files changed, 184 insertions(+), 15 deletions(-) create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md create mode 100644 test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/AspNetCoreHostTestTest.cs create mode 100644 test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs create mode 100644 test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolOptions.cs create mode 100644 test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj diff --git a/Cuemon.sln b/Cuemon.sln index c124fa66a..175968961 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -129,6 +129,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Data.SqlClient.Tests EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore", "src\Cuemon.Extensions.Xunit.Hosting.AspNetCore\Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj", "{200BDF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests", "test\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj", "{72422689-CDC3-4AD6-89D7-25F85545B0FE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -379,6 +381,10 @@ Global {200BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.Build.0 = Debug|Any CPU {200BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.ActiveCfg = Release|Any CPU {200BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.Build.0 = Release|Any CPU + {72422689-CDC3-4AD6-89D7-25F85545B0FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72422689-CDC3-4AD6-89D7-25F85545B0FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72422689-CDC3-4AD6-89D7-25F85545B0FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72422689-CDC3-4AD6-89D7-25F85545B0FE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -445,6 +451,7 @@ Global {8A3E26BD-A3C4-4684-909B-1ABDFDB4108D} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {A9610C9E-1944-4771-A5E1-CE47ADA243D5} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {200BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} + {72422689-CDC3-4AD6-89D7-25F85545B0FE} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2A848386-B682-4F6D-8254-B5F6247C3054} diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md new file mode 100644 index 000000000..78f807bf3 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md @@ -0,0 +1,18 @@ +--- +uid: Cuemon.Extensions.Xunit.Hosting.AspNetCore +summary: *content +--- +The Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.TestHost namespace + +Availability: NET Core 3.1 + +Complements: [Microsoft.AspNetCore.TestHost namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.testhost?view=aspnetcore-3.0) 🔗 + +Github branches: 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore) + +NuGet packages 📦\ +[Cuemon.Extensions.Xunit.Hosting.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Xunit.Hosting.AspNetCore)\ +[Cuemon.Extensions.Xunit.Hosting.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Xunit.Hosting.AspNetCore) \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostTest.cs index c56da5aba..1dcd9b2c7 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostTest.cs @@ -1,7 +1,4 @@ -using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Hosting; using Xunit.Abstractions; @@ -40,6 +37,7 @@ protected override void InitializeHostFixture(T hostFixture) Host = hostFixture.Host; ServiceProvider = hostFixture.Host.Services; Application = hostFixture.Application; + Configure(hostFixture.Configuration, hostFixture.HostingEnvironment); } /// @@ -48,15 +46,6 @@ protected override void InitializeHostFixture(T hostFixture) /// The initialized by the . public IApplicationBuilder Application { get; protected set; } - /// - /// Adds services to the container. - /// - /// The collection of service descriptors. - public override void ConfigureServices(IServiceCollection services) - { - services.AddTransient(); - } - /// /// Configures the HTTP request pipeline. /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj index dde37d86c..a90e43d46 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Xunit.Hosting.AspNetCore Cuemon.Extensions.Xunit.Hosting.AspNetCore - The Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. + The Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.TestHost namespace. asp-net-core-host-test class-fixture asp-net-core-host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services test-server diff --git a/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs index 124404c20..93d67e08a 100644 --- a/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Cuemon.Extensions.AspNetCore.Builder; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; using Microsoft.AspNetCore.Builder; using Xunit; using Xunit.Abstractions; @@ -38,7 +39,7 @@ public async Task InvokeAsync_ShouldHaveHostingEnvironmentHeader_ConfiguredByIOp public override void ConfigureServices(IServiceCollection services) { - base.ConfigureServices(services); + services.AddTransient(); services.Configure(o => o.HeaderName = "X-Environment"); } diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs index ef5943ae5..a48ea431d 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Cuemon.Extensions.AspNetCore.Builder; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; using Cuemon.Text; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -58,6 +59,15 @@ public async Task InvokeAsync_ShouldRelayCorrelationIdHeader_ConfiguredByDefault TestOutput.WriteLine(expected); } + /// + /// Adds services to the container. + /// + /// The collection of service descriptors. + public override void ConfigureServices(IServiceCollection services) + { + services.AddTransient(); + } + public override void ConfigureApplication(IApplicationBuilder app) { app.UseCorrelationIdentifier(); diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs index 198d5b465..df08592a1 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Cuemon.Extensions.AspNetCore.Builder; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; using Cuemon.Messaging; using Cuemon.Text; using Microsoft.AspNetCore.Builder; @@ -67,6 +68,15 @@ public async Task InvokeAsync_ShouldIgnoreExistingRequestIdHeader_ConfiguredByDe TestOutput.WriteLine(requestId); } + /// + /// Adds services to the container. + /// + /// The collection of service descriptors. + public override void ConfigureServices(IServiceCollection services) + { + services.AddTransient(); + } + public override void ConfigureApplication(IApplicationBuilder app) { app.UseRequestIdentifier(o => o.RequestProvider = () => DynamicRequest.Create(Generate.RandomString(32, Alphanumeric.PunctuationMarks, Alphanumeric.Numbers))); diff --git a/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/AspNetCoreHostTestTest.cs b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/AspNetCoreHostTestTest.cs new file mode 100644 index 000000000..3a8cd2c90 --- /dev/null +++ b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/AspNetCoreHostTestTest.cs @@ -0,0 +1,63 @@ +using System; +using System.Threading.Tasks; +using Cuemon.Extensions.IO; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Assets; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +{ + public class AspNetCoreHostTestTest : AspNetCoreHostTest + { + private readonly IServiceProvider _provider; + private readonly IApplicationBuilder _pipeline; + + public AspNetCoreHostTestTest(AspNetCoreHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _pipeline = hostFixture.Application; + _provider = hostFixture.ServiceProvider; + } + + [Fact] + public async Task ShouldHaveResultOfBoolMiddlewareInBody() + { + var context = _provider.GetRequiredService().HttpContext; + var options = _provider.GetRequiredService>(); + var pipeline = _pipeline.Build(); + + Assert.Equal("", context.Response.Body.ToEncodedString(o => o.LeaveOpen = true)); + + await pipeline(context); + + Assert.Equal("A:True, B:False, C:True, D:False, E:True, F:False", context.Response.Body.ToEncodedString()); + + Assert.True(options.Value.A); + Assert.False(options.Value.B); + Assert.True(options.Value.C); + Assert.False(options.Value.D); + Assert.True(options.Value.E); + Assert.False(options.Value.F); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + app.UseMiddleware(); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddTransient(); + services.Configure(o => + { + o.A = true; + o.C = true; + o.E = true; + }); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs new file mode 100644 index 000000000..d35619c7e --- /dev/null +++ b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading.Tasks; +using Cuemon.AspNetCore; +using Cuemon.AspNetCore.Http.Headers; +using Cuemon.Collections.Generic; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Assets +{ + public class BoolMiddleware : ConfigurableMiddleware + { + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + /// The which need to be configured. + public BoolMiddleware(RequestDelegate next, IOptions setup) : base(next, setup) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + /// The which need to be configured. + public BoolMiddleware(RequestDelegate next, Action setup) : base(next, setup) + { + } + + /// + /// Executes the . + /// + /// The context of the current request. + /// A task that represents the execution of this middleware. + public override async Task InvokeAsync(HttpContext context) + { + await context.Response.WriteAsync($"A:{Options.A}, B:{Options.B}, C:{Options.C}, D:{Options.D}, E:{Options.E}, F:{Options.F}"); + await Next(context); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolOptions.cs b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolOptions.cs new file mode 100644 index 000000000..ce60911aa --- /dev/null +++ b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolOptions.cs @@ -0,0 +1,17 @@ +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Assets +{ + public class BoolOptions + { + public bool A { get; set; } + + public bool B { get; set; } + + public bool C { get; set; } + + public bool D { get; set; } + + public bool E { get; set; } + + public bool F { get; set; } + } +} \ No newline at end of file diff --git a/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj new file mode 100644 index 000000000..f5bd81dba --- /dev/null +++ b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj @@ -0,0 +1,12 @@ + + + + Cuemon.Extensions.Xunit.Hosting.AspNetCore + + + + + + + + \ No newline at end of file From ac9fe257d922a1cae823d8813c458117a8649c17 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 14 Oct 2020 00:14:17 +0200 Subject: [PATCH 307/385] Added release notes. --- .../Http/FakeHttpContextAccessor.cs | 2 +- .../Properties/AssemblyInfo.cs | 4 ++++ .../Properties/PackageReleaseNotes.txt | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/AssemblyInfo.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/PackageReleaseNotes.txt diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs index 78e9ca311..fe2dc7e60 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs @@ -6,7 +6,7 @@ namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http { /// - /// Provides a unit test implementation of .. + /// Provides a unit test implementation of . /// /// public class FakeHttpContextAccessor : IHttpContextAccessor diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/AssemblyInfo.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d734d0852 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("eecbaeec-0593-4e91-9ef3-417e986bc341")] \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..3344ffc50 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/PackageReleaseNotes.txt @@ -0,0 +1,14 @@ +Version: 6.0.0 +Availability: NET Core 3.1 +  +# New Features +- ADDED FakeHttpResponseFeature class in the Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features namespace that represents a way to trigger IHttpResponseFeature.OnStarting +- ADDED FakeHttpResponseMiddleware class in the Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features namespace that provides a fake HTTP response middleware implementation for ASP.NET Core testing +- ADDED FakeHttpContextAccessor class in the Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http namespace that provides a unit test implementation of IHttpContextAccessor +- ADDED ApplicationBuilderExtensions class in the Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace that consist of extension methods for the IApplicationBuilder interface: UseFakeHttpResponseTrigger +- ADDED AspNetCoreHostFixture class in the Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace that provides a default implementation of the IAspNetCoreHostFixture interface +- ADDED AspNetCoreHostTest{T} class in the Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace that represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection and depends on ASP.NET Core, should derive +- ADDED IAspNetCoreHostFixture interface in the Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace that provides a way to use Microsoft Dependency Injection in unit tests tailored for ASP.NET Core +- ADDED IMiddlewareTest interface in the Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace that represents the members needed for ASP.NET Core middleware testing +- ADDED IPipelineTest interface in the Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace that represents the members needed for ASP.NET Core pipeline testing +- ADDED MiddlewareTestFactory class in the Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace that provides a set of static methods for ASP.NET Core middleware unit testing \ No newline at end of file From 496de9c266f4076f86322e072c4671ed06e536d7 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 14 Oct 2020 00:27:17 +0200 Subject: [PATCH 308/385] Removed InternalsVisibleTo and made shared methods public (with a sidenote, that not intended to be used in your code). --- .../IO/StreamDecoratorExtensions.cs | 35 ++++++++++++++++--- src/Cuemon.Core/Security/Hash.cs | 2 +- src/Cuemon.Core/Text/ByteOrderMark.cs | 2 +- .../Extensions/StreamDecoratorExtensions.cs | 18 +--------- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/Cuemon.Core/Extensions/IO/StreamDecoratorExtensions.cs b/src/Cuemon.Core/Extensions/IO/StreamDecoratorExtensions.cs index 64324a327..1e23c432f 100644 --- a/src/Cuemon.Core/Extensions/IO/StreamDecoratorExtensions.cs +++ b/src/Cuemon.Core/Extensions/IO/StreamDecoratorExtensions.cs @@ -1,11 +1,29 @@ -using System.IO; +using System; +using System.IO; namespace Cuemon.IO { - internal static class StreamDecoratorExtensions + /// + /// Extension methods for the class tailored to adhere the decorator pattern. + /// This API supports the product infrastructure and is not intended to be used directly from your code. + /// + /// + /// + public static class StreamDecoratorExtensions { - internal static void CopyStreamCore(this IDecorator decorator, Stream destination, int bufferSize = 81920, bool changePosition = true) + /// + /// Reads the bytes from the enclosed of the specified and writes them to the . + /// + /// The to extend. + /// The to which the contents of the current stream will be copied. + /// The size of the buffer. This value must be greater than zero. The default size is 81920. + /// if true, the enclosed of the specified will temporarily have its position changed to 0; otherwise the position is left untouched. + /// + /// cannot be null. + /// + public static void CopyStream(this IDecorator decorator, Stream destination, int bufferSize = 81920, bool changePosition = true) { + Validator.ThrowIfNull(decorator, nameof(decorator)); var source = decorator.Inner; long lastPosition = 0; if (changePosition && source.CanSeek) @@ -21,7 +39,16 @@ internal static void CopyStreamCore(this IDecorator decorator, Stream de if (changePosition && destination.CanSeek) { destination.Position = 0; } } - internal static byte[] ToByteArrayCore(this IDecorator decorator, int bufferSize = 81920, bool leaveOpen = false) + /// + /// Converts the enclosed of the specified to its equivalent representation. Not intended to be used directly from your code. + /// + /// The to extend. + /// The size of the buffer. This value must be greater than zero. The default size is 81920. + /// if true, the object is being left open; otherwise it is being closed and disposed. + /// + /// cannot be null. + /// + public static byte[] InvokeToByteArray(this IDecorator decorator, int bufferSize = 81920, bool leaveOpen = false) { Validator.ThrowIfNull(decorator, nameof(decorator)); Validator.ThrowIfFalse(decorator.Inner.CanRead, nameof(decorator.Inner), "Stream cannot be read from."); diff --git a/src/Cuemon.Core/Security/Hash.cs b/src/Cuemon.Core/Security/Hash.cs index 0a498c545..c64b10d17 100644 --- a/src/Cuemon.Core/Security/Hash.cs +++ b/src/Cuemon.Core/Security/Hash.cs @@ -266,7 +266,7 @@ public virtual HashResult ComputeHash(Stream input) { return ComputeHash(Patterns.SafeInvoke(() => new MemoryStream(), destination => { - Decorator.Enclose(input).CopyStreamCore(destination); + Decorator.Enclose(input).CopyStream(destination); return destination; }).ToArray()); } diff --git a/src/Cuemon.Core/Text/ByteOrderMark.cs b/src/Cuemon.Core/Text/ByteOrderMark.cs index ee5078af3..07724219e 100644 --- a/src/Cuemon.Core/Text/ByteOrderMark.cs +++ b/src/Cuemon.Core/Text/ByteOrderMark.cs @@ -156,7 +156,7 @@ public static Stream Remove(Stream value, Encoding encoding, Action new MemoryStream(bytes.Length), ms => { diff --git a/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs b/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs index 265a526be..aa3611072 100644 --- a/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs +++ b/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs @@ -15,22 +15,6 @@ namespace Cuemon.IO /// public static class StreamDecoratorExtensions { - /// - /// Reads the bytes from the enclosed of the specified and writes them to the . - /// - /// The to extend. - /// The to which the contents of the current stream will be copied. - /// The size of the buffer. This value must be greater than zero. The default size is 81920. - /// if true, the enclosed of the specified will temporarily have its position changed to 0; otherwise the position is left untouched. - /// - /// cannot be null. - /// - public static void CopyStream(this IDecorator decorator, Stream destination, int bufferSize = 81920, bool changePosition = true) - { - Validator.ThrowIfNull(decorator, nameof(decorator)); - decorator.CopyStreamCore(destination, bufferSize, changePosition); - } - /// /// Asynchronously reads the bytes from the enclosed of the specified and writes them to the . /// @@ -77,7 +61,7 @@ public static byte[] ToByteArray(this IDecorator decorator, Action From 686c4ded0af7d64cc3f4616e1024276636286cf7 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 14 Oct 2020 00:27:41 +0200 Subject: [PATCH 309/385] Removed InternalsVisibleTo and made shared methods public (with a sidenote, that not intended to be used in your code). --- src/Cuemon.Core/Properties/AssemblyInfo.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Cuemon.Core/Properties/AssemblyInfo.cs b/src/Cuemon.Core/Properties/AssemblyInfo.cs index ddd438e05..a905199ee 100644 --- a/src/Cuemon.Core/Properties/AssemblyInfo.cs +++ b/src/Cuemon.Core/Properties/AssemblyInfo.cs @@ -1,6 +1,4 @@ -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; [assembly: ComVisible(false)] -[assembly: InternalsVisibleTo("Cuemon.IO, PublicKey=00240000048000009400000006020000002400005253413100040000010001002F66D8473F676F4E7B47400527D33951A774422DFFC3DF6D7F87C82E5694E9F3AA626D36BEBEA428AD5B800EFCF6CE87B73268F5A0125A7D38739D344703A1C48785AC1A45B1C27EDFDF2EB30BA2B3E3CEA92E5981C30F3A95685A680B7EBEE66F422D176CD1623019D5A05770B9BA498144B1134593BEA6F674F334CF2B90B0")] [assembly: Guid("989939cf-cef2-4e23-8bce-725255e35ce6")] \ No newline at end of file From dcdc01be49f7ba45d365c85996a278b8b6ac2215 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 14 Oct 2020 00:42:30 +0200 Subject: [PATCH 310/385] Update azure-pipelines.yml for Azure Pipelines Have to increase timeout; sometime it runs fast - other times slow. The downside of shared cloud instances. --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0ad62dd6f..658ec7636 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,7 +16,7 @@ variables: jobs: - job: CI - timeoutInMinutes: 75 + timeoutInMinutes: 120 strategy: matrix: From 47622048b2742ad2780fc25f6f2824da6e995a8b Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 14 Oct 2020 15:57:14 +0200 Subject: [PATCH 311/385] Extended with overloads that supports the implementation factory pattern, --- .../ServiceCollectionExtensions.cs | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/src/Cuemon.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.DependencyInjection/ServiceCollectionExtensions.cs index 09e687f9b..a9db04406 100644 --- a/src/Cuemon.Extensions.DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Cuemon.Extensions.DependencyInjection/ServiceCollectionExtensions.cs @@ -163,5 +163,168 @@ private static void AddServices(this IServiceCollection services, Type service, break; } } + + /// + /// Adds the specified with the and to the . + /// + /// The type of the service to add. + /// The type of the implementation to use. + /// The to add the service to. + /// The function delegate that creates the service. + /// The lifetime of the service. + /// A reference to after the operation has completed. + public static IServiceCollection Add(this IServiceCollection services, Func implementationFactory, ServiceLifetime lifetime) + where TService : class + where TImplementation : class, TService + { + return services.Add(typeof(TService), implementationFactory, lifetime); + } + + /// + /// Adds the specified with the and to the . + /// + /// The type of the service to add. + /// The type of the implementation to use. + /// The type of the configured options. + /// The to add the service to. + /// The function delegate that creates the service. + /// The lifetime of the service. + /// The which need to be configured. + /// A reference to after the operation has completed. + public static IServiceCollection Add(this IServiceCollection services, Func implementationFactory, ServiceLifetime lifetime, Action setup) + where TService : class + where TImplementation : class, TService + where TOptions : class, new() + { + return services.Add(typeof(TService), implementationFactory, lifetime, setup); + } + + /// + /// Adds the specified with the and to the . + /// + /// The to add the service to. + /// The type of the service to register. + /// The function delegate that creates the service. + /// The lifetime of the service. + /// A reference to after the operation has completed. + public static IServiceCollection Add(this IServiceCollection services, Type service, Func implementationFactory, ServiceLifetime lifetime) + { + Validator.ThrowIfNull(services, nameof(services)); + services.AddServices(service, implementationFactory, lifetime, false); + return services; + } + + /// + /// Adds the specified with the and to the . + /// + /// The type of the configured options. + /// The to add the service to. + /// The type of the service to register. + /// The function delegate that creates the service. + /// The lifetime of the service. + /// The which need to be configured. + /// A reference to after the operation has completed. + public static IServiceCollection Add(this IServiceCollection services, Type service, Func implementationFactory, ServiceLifetime lifetime, Action setup) + where TOptions : class, new() + { + Validator.ThrowIfNull(services, nameof(services)); + Validator.ThrowIfNull(setup, nameof(setup)); + services.AddServices(service, implementationFactory, lifetime, false); + services.Configure(setup); + return services; + } + + /// + /// Adds the specified with the and to the if the service type has not already been registered. + /// + /// The type of the service to add. + /// The type of the implementation to use. + /// The to add the service to. + /// The function delegate that creates the service. + /// The lifetime of the service. + /// A reference to after the operation has completed. + public static IServiceCollection TryAdd(this IServiceCollection services, Func implementationFactory, ServiceLifetime lifetime) + where TService : class + where TImplementation : class, TService + { + Validator.ThrowIfNull(services, nameof(services)); + Validator.ThrowIfNull(implementationFactory, nameof(implementationFactory)); + return services.TryAdd(typeof(TService), implementationFactory, lifetime); + } + + /// + /// Adds the specified with the and to the if the service type has not already been registered. + /// + /// The type of the service to add. + /// The type of the implementation to use. + /// The type of the configured options. + /// The to add the service to. + /// The function delegate that creates the service. + /// The lifetime of the service. + /// The which need to be configured. + /// A reference to after the operation has completed. + public static IServiceCollection TryAdd(this IServiceCollection services, Func implementationFactory, ServiceLifetime lifetime, Action setup) + where TService : class + where TImplementation : class, TService + where TOptions : class, new() + { + Validator.ThrowIfNull(services, nameof(services)); + Validator.ThrowIfNull(implementationFactory, nameof(implementationFactory)); + Validator.ThrowIfNull(setup, nameof(setup)); + return services.TryAdd(typeof(TService), implementationFactory, lifetime, setup); + } + + /// + /// Adds the specified with the and to the if the service type has not already been registered. + /// + /// The to add the service to. + /// The type of the service to register. + /// The function delegate that creates the service. + /// The lifetime of the service. + /// A reference to after the operation has completed. + public static IServiceCollection TryAdd(this IServiceCollection services, Type service, Func implementationFactory, ServiceLifetime lifetime) + { + Validator.ThrowIfNull(services, nameof(services)); + Validator.ThrowIfNull(implementationFactory, nameof(implementationFactory)); + services.AddServices(service, implementationFactory, lifetime, true); + return services; + } + + /// + /// Adds the specified with the and to the if the service type has not already been registered. + /// + /// The type of the configured options. + /// The to add the service to. + /// The type of the service to register. + /// The function delegate that creates the service. + /// The lifetime of the service. + /// The which need to be configured. + /// A reference to after the operation has completed. + public static IServiceCollection TryAdd(this IServiceCollection services, Type service, Func implementationFactory, ServiceLifetime lifetime, Action setup) + where TOptions : class, new() + { + Validator.ThrowIfNull(services, nameof(services)); + Validator.ThrowIfNull(implementationFactory, nameof(implementationFactory)); + Validator.ThrowIfNull(setup, nameof(setup)); + services.AddServices(service, implementationFactory, lifetime, true); + services.Configure(setup); + return services; + } + + private static void AddServices(this IServiceCollection services, Type service, Func implementationFactory, ServiceLifetime lifetime, bool useTesterDoerPattern) + { + switch (lifetime) + { + case ServiceLifetime.Scoped: + Condition.FlipFlop(useTesterDoerPattern, () => services.TryAddScoped(service, implementationFactory), () => services.AddScoped(service, implementationFactory)); + break; + case ServiceLifetime.Singleton: + Condition.FlipFlop(useTesterDoerPattern, () => services.TryAddSingleton(service, implementationFactory), () => services.AddSingleton(service, implementationFactory)); + break; + case ServiceLifetime.Transient: + Condition.FlipFlop(useTesterDoerPattern, () => services.TryAddTransient(service, implementationFactory), () => services.AddTransient(service, implementationFactory)); + break; + } + } } } \ No newline at end of file From c8fe6d0b436483ce0f855fed434db1975fac2e23 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 14 Oct 2020 15:59:12 +0200 Subject: [PATCH 312/385] Alignment and AddHttpContextAccessor extension method. --- .../AspNetCoreHostFixture.cs | 1 - ...Extensions.Xunit.Hosting.AspNetCore.csproj | 1 + .../Http/FakeHttpContextAccessor.cs | 14 +++----- .../ServiceCollectionExtensions.cs | 35 +++++++++++++++++++ .../HostFixture.cs | 5 +-- 5 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs index 15091f8c9..580e048d5 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs @@ -50,7 +50,6 @@ public override void ConfigureHost(Test hostTest) .UseEnvironment("Development") .ConfigureAppConfiguration((context, config) => { - config.AddEnvironmentVariables("ASPNETCORE_"); config.AddJsonFile("appsettings.json", true, true) .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) .AddEnvironmentVariables(); diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj index a90e43d46..3d7a695ee 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj @@ -18,6 +18,7 @@ + \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs index fe2dc7e60..b82dc9781 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; @@ -11,7 +12,6 @@ namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http /// public class FakeHttpContextAccessor : IHttpContextAccessor { - private HttpContext _httpContextCurrent; /// /// Initializes a new instance of the class. @@ -21,18 +21,14 @@ public FakeHttpContextAccessor() var fc = new FeatureCollection(); fc.Set(new FakeHttpResponseFeature()); fc.Set(new HttpRequestFeature()); - _httpContextCurrent = new DefaultHttpContext(fc); - _httpContextCurrent.Response.Body = new MemoryStream(); + HttpContext = new DefaultHttpContext(fc); + HttpContext.Response.Body = new MemoryStream(); } /// /// Gets or sets the HTTP context. /// /// The HTTP context. - public HttpContext HttpContext - { - get => _httpContextCurrent; - set => _httpContextCurrent = value; - } + public HttpContext HttpContext { get; set; } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..4db4b9106 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs @@ -0,0 +1,35 @@ +using Cuemon.Extensions.DependencyInjection; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore +{ + /// + /// Extension methods for the interface. + /// + public static class ServiceCollectionExtensions + { + /// + /// Adds a unit test optimized implementation for the service. + /// + /// The to extend. + /// The lifetime of the service. + /// A reference to after the operation has completed. + public static IServiceCollection AddHttpContextAccessor(this IServiceCollection services, ServiceLifetime lifetime) + { + services.TryAdd(provider => + { + var contextAccessor = new FakeHttpContextAccessor + { + HttpContext = + { + RequestServices = provider + } + }; + return contextAccessor; + }, lifetime); + return services; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs index eae19ee66..8465a75f1 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs @@ -38,10 +38,11 @@ public virtual void ConfigureHost(Test hostTest) Validator.ThrowIfNotContainsType(hostTestType, nameof(hostTestType), $"{nameof(hostTest)} is not assignable from HostTest.", typeof(HostTest<>)); Host = new HostBuilder() - .ConfigureHostConfiguration(config => config.AddEnvironmentVariables("DOTNET_")) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseEnvironment("Development") .ConfigureAppConfiguration((context, config) => { - config.SetBasePath(Directory.GetCurrentDirectory()) + config .AddJsonFile("appsettings.json", true, true) .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) .AddEnvironmentVariables(); From 410f77553484631d9871e00894d72421d2608512 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 14 Oct 2020 15:59:57 +0200 Subject: [PATCH 313/385] Adopted new extension method (AddHttpContextAccessor). --- .../Hosting/HostingEnvironmentMiddlewareTest.cs | 3 +-- .../Http/Headers/CorrelationIdentifierMiddlewareTest.cs | 3 +-- .../Http/Headers/RequestIdentifierMiddlewareTest.cs | 3 +-- .../Http/Headers/UserAgentSentinelMiddlewareTest.cs | 9 ++++----- .../Http/Throttling/ThrottlingSentinelMiddlewareTest.cs | 5 ++--- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs index 93d67e08a..1c4ad5cbf 100644 --- a/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Cuemon.Extensions.AspNetCore.Builder; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; using Microsoft.AspNetCore.Builder; using Xunit; using Xunit.Abstractions; @@ -39,7 +38,7 @@ public async Task InvokeAsync_ShouldHaveHostingEnvironmentHeader_ConfiguredByIOp public override void ConfigureServices(IServiceCollection services) { - services.AddTransient(); + services.AddHttpContextAccessor(ServiceLifetime.Transient); services.Configure(o => o.HeaderName = "X-Environment"); } diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs index a48ea431d..db3a9bdef 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Cuemon.Extensions.AspNetCore.Builder; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; using Cuemon.Text; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -65,7 +64,7 @@ public async Task InvokeAsync_ShouldRelayCorrelationIdHeader_ConfiguredByDefault /// The collection of service descriptors. public override void ConfigureServices(IServiceCollection services) { - services.AddTransient(); + services.AddHttpContextAccessor(ServiceLifetime.Transient); } public override void ConfigureApplication(IApplicationBuilder app) diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs index df08592a1..c202b2ce9 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Cuemon.Extensions.AspNetCore.Builder; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; using Cuemon.Messaging; using Cuemon.Text; using Microsoft.AspNetCore.Builder; @@ -74,7 +73,7 @@ public async Task InvokeAsync_ShouldIgnoreExistingRequestIdHeader_ConfiguredByDe /// The collection of service descriptors. public override void ConfigureServices(IServiceCollection services) { - services.AddTransient(); + services.AddHttpContextAccessor(ServiceLifetime.Transient); } public override void ConfigureApplication(IApplicationBuilder app) diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs index 8d95949bd..d97d3f1ae 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs @@ -4,7 +4,6 @@ using Cuemon.Extensions.IO; using Cuemon.Extensions.Xunit; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -30,7 +29,7 @@ public async Task InvokeAsync_ShouldThrowUserAgentException_BadRequest() }, services => { services.Configure(o => { o.RequireUserAgentHeader = true; }); - services.AddScoped(); + services.AddHttpContextAccessor(ServiceLifetime.Scoped); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; @@ -63,7 +62,7 @@ public async Task InvokeAsync_ShouldThrowUserAgentException_Forbidden() o.ValidateUserAgentHeader = true; o.AllowedUserAgents.Add("Cuemon-Agent"); }); - services.AddScoped(); + services.AddHttpContextAccessor(ServiceLifetime.Scoped); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; @@ -101,7 +100,7 @@ public async Task InvokeAsync_ShouldThrowUserAgentException_BadRequest_BecauseOf o.UseGenericResponse = true; o.AllowedUserAgents.Add("Cuemon-Agent"); }); - services.AddScoped(); + services.AddHttpContextAccessor(ServiceLifetime.Scoped); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; @@ -159,7 +158,7 @@ public async Task InvokeAsync_ShouldAllowRequestAfterBeingValidated() o.ValidateUserAgentHeader = true; o.AllowedUserAgents.Add("Cuemon-Agent"); }); - services.AddScoped(); + services.AddHttpContextAccessor(ServiceLifetime.Scoped); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; diff --git a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs index ec4f7ceab..7ee59ffeb 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs @@ -5,7 +5,6 @@ using Cuemon.Extensions.IO; using Cuemon.Extensions.Xunit; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -34,7 +33,7 @@ public async Task InvokeAsync_ShouldThrowThrottlingException_TooManyRequests() o.Quota = new ThrottleQuota(10, TimeSpan.FromMinutes(5)); o.ContextResolver = cr => nameof(ThrottlingSentinelMiddlewareTest); }); - services.AddSingleton(); + services.AddHttpContextAccessor(ServiceLifetime.Singleton); services.AddMemoryThrottlingCache(); })) { @@ -79,7 +78,7 @@ public async Task InvokeAsync_ShouldRehydrate() o.Quota = new ThrottleQuota(10, window); o.ContextResolver = cr => nameof(ThrottlingSentinelMiddlewareTest); }); - services.AddSingleton(); + services.AddHttpContextAccessor(ServiceLifetime.Singleton); services.AddMemoryThrottlingCache(); })) { From 7bc26b50c532583dc265517e1738d5b4724eb792 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 14 Oct 2020 16:00:16 +0200 Subject: [PATCH 314/385] Changed default environment to Development. --- test/Cuemon.Extensions.Xunit.Hosting.Tests/HostTestTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Cuemon.Extensions.Xunit.Hosting.Tests/HostTestTest.cs b/test/Cuemon.Extensions.Xunit.Hosting.Tests/HostTestTest.cs index 65ad8dfca..950999400 100644 --- a/test/Cuemon.Extensions.Xunit.Hosting.Tests/HostTestTest.cs +++ b/test/Cuemon.Extensions.Xunit.Hosting.Tests/HostTestTest.cs @@ -65,9 +65,9 @@ public void Test_ShouldHaveConfigurationEntry() } [Fact] - public void Test_ShouldHaveEnvironmentOfroduction() + public void Test_ShouldHaveEnvironmentOfDevelopment() { - Assert.Equal("Production", HostingEnvironment.EnvironmentName); + Assert.Equal("Development", HostingEnvironment.EnvironmentName); } public override void ConfigureServices(IServiceCollection services) From e079270a92bcf074c8820a908caec94466d0f749 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 00:50:57 +0200 Subject: [PATCH 315/385] ServiceProvider now always point to Host.Services. --- src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs index 8465a75f1..85aca9540 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/HostFixture.cs @@ -54,7 +54,6 @@ public virtual void ConfigureHost(Test hostTest) Configuration = context.Configuration; HostingEnvironment = context.HostingEnvironment; ConfigureServicesCallback(services); - ServiceProvider = services.BuildServiceProvider(); }).Build(); } @@ -90,7 +89,7 @@ public virtual void ConfigureHost(Test hostTest) /// Gets the initialized by this instance. /// /// The initialized by this instance. - public IServiceProvider ServiceProvider { get; protected set; } + public IServiceProvider ServiceProvider => Host.Services; /// /// Gets the initialized by this instance. From f74faffb3e65ba5957b6655a81255f68ce7b6514 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 00:53:40 +0200 Subject: [PATCH 316/385] Consequence change of e079270a. --- .../AspNetCoreHostFixture.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs index 580e048d5..4b192519d 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/AspNetCoreHostFixture.cs @@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -68,7 +67,6 @@ public override void ConfigureHost(Test hostTest) Configuration = context.Configuration; HostingEnvironment = context.HostingEnvironment; ConfigureServicesCallback(services); - ServiceProvider = services.BuildServiceProvider(); }) .Configure(app => { From e0c3a89290b15be200a7d0de7bfc6bc53b15749a Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 00:54:02 +0200 Subject: [PATCH 317/385] Made the intend more clear due to naming conflict. --- .../ServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs index 4db4b9106..3d105b9f2 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs @@ -16,7 +16,7 @@ public static class ServiceCollectionExtensions /// The to extend. /// The lifetime of the service. /// A reference to after the operation has completed. - public static IServiceCollection AddHttpContextAccessor(this IServiceCollection services, ServiceLifetime lifetime) + public static IServiceCollection AddFakeHttpContextAccessor(this IServiceCollection services, ServiceLifetime lifetime) { services.TryAdd(provider => { From c88bb75f5818f9b1638105cbd2c036ce082eaa30 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 00:54:41 +0200 Subject: [PATCH 318/385] Consequence change of e0c3a892. --- .../Hosting/HostingEnvironmentMiddlewareTest.cs | 2 +- .../Http/Headers/CorrelationIdentifierMiddlewareTest.cs | 2 +- .../Http/Headers/RequestIdentifierMiddlewareTest.cs | 2 +- .../Http/Headers/UserAgentSentinelMiddlewareTest.cs | 8 ++++---- .../Http/Throttling/ThrottlingSentinelMiddlewareTest.cs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs index 1c4ad5cbf..23fdf5387 100644 --- a/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs @@ -38,7 +38,7 @@ public async Task InvokeAsync_ShouldHaveHostingEnvironmentHeader_ConfiguredByIOp public override void ConfigureServices(IServiceCollection services) { - services.AddHttpContextAccessor(ServiceLifetime.Transient); + services.AddFakeHttpContextAccessor(ServiceLifetime.Transient); services.Configure(o => o.HeaderName = "X-Environment"); } diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs index db3a9bdef..5b0abccc6 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs @@ -64,7 +64,7 @@ public async Task InvokeAsync_ShouldRelayCorrelationIdHeader_ConfiguredByDefault /// The collection of service descriptors. public override void ConfigureServices(IServiceCollection services) { - services.AddHttpContextAccessor(ServiceLifetime.Transient); + services.AddFakeHttpContextAccessor(ServiceLifetime.Transient); } public override void ConfigureApplication(IApplicationBuilder app) diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs index c202b2ce9..d745a20e5 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs @@ -73,7 +73,7 @@ public async Task InvokeAsync_ShouldIgnoreExistingRequestIdHeader_ConfiguredByDe /// The collection of service descriptors. public override void ConfigureServices(IServiceCollection services) { - services.AddHttpContextAccessor(ServiceLifetime.Transient); + services.AddFakeHttpContextAccessor(ServiceLifetime.Transient); } public override void ConfigureApplication(IApplicationBuilder app) diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs index d97d3f1ae..b557792df 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs @@ -29,7 +29,7 @@ public async Task InvokeAsync_ShouldThrowUserAgentException_BadRequest() }, services => { services.Configure(o => { o.RequireUserAgentHeader = true; }); - services.AddHttpContextAccessor(ServiceLifetime.Scoped); + services.AddFakeHttpContextAccessor(ServiceLifetime.Scoped); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; @@ -62,7 +62,7 @@ public async Task InvokeAsync_ShouldThrowUserAgentException_Forbidden() o.ValidateUserAgentHeader = true; o.AllowedUserAgents.Add("Cuemon-Agent"); }); - services.AddHttpContextAccessor(ServiceLifetime.Scoped); + services.AddFakeHttpContextAccessor(ServiceLifetime.Scoped); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; @@ -100,7 +100,7 @@ public async Task InvokeAsync_ShouldThrowUserAgentException_BadRequest_BecauseOf o.UseGenericResponse = true; o.AllowedUserAgents.Add("Cuemon-Agent"); }); - services.AddHttpContextAccessor(ServiceLifetime.Scoped); + services.AddFakeHttpContextAccessor(ServiceLifetime.Scoped); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; @@ -158,7 +158,7 @@ public async Task InvokeAsync_ShouldAllowRequestAfterBeingValidated() o.ValidateUserAgentHeader = true; o.AllowedUserAgents.Add("Cuemon-Agent"); }); - services.AddHttpContextAccessor(ServiceLifetime.Scoped); + services.AddFakeHttpContextAccessor(ServiceLifetime.Scoped); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; diff --git a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs index 7ee59ffeb..73ed16f7a 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs @@ -33,7 +33,7 @@ public async Task InvokeAsync_ShouldThrowThrottlingException_TooManyRequests() o.Quota = new ThrottleQuota(10, TimeSpan.FromMinutes(5)); o.ContextResolver = cr => nameof(ThrottlingSentinelMiddlewareTest); }); - services.AddHttpContextAccessor(ServiceLifetime.Singleton); + services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); services.AddMemoryThrottlingCache(); })) { @@ -78,7 +78,7 @@ public async Task InvokeAsync_ShouldRehydrate() o.Quota = new ThrottleQuota(10, window); o.ContextResolver = cr => nameof(ThrottlingSentinelMiddlewareTest); }); - services.AddHttpContextAccessor(ServiceLifetime.Singleton); + services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); services.AddMemoryThrottlingCache(); })) { From 2a811b90735b176d4deb635e0d9da2ccd86a3800 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 00:55:32 +0200 Subject: [PATCH 319/385] Unit testing of Mvc assembly. --- .../BearerThrottlingSentinelAttribute.cs | 23 ++++ .../Assets/FakeController.cs | 16 +++ .../Cuemon.AspNetCore.Mvc.Tests.csproj | 14 ++- .../ThrottlingSentinelAttributeTest.cs | 105 ++++++++++++++++++ .../SeeOtherResultTest.cs | 47 ++++++++ .../xunit.runner.json | 3 + 6 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 test/Cuemon.AspNetCore.Mvc.Tests/Assets/BearerThrottlingSentinelAttribute.cs create mode 100644 test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs create mode 100644 test/Cuemon.AspNetCore.Mvc.Tests/Filters/Throttling/ThrottlingSentinelAttributeTest.cs create mode 100644 test/Cuemon.AspNetCore.Mvc.Tests/SeeOtherResultTest.cs create mode 100644 test/Cuemon.AspNetCore.Mvc.Tests/xunit.runner.json diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Assets/BearerThrottlingSentinelAttribute.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/BearerThrottlingSentinelAttribute.cs new file mode 100644 index 000000000..91c6cb9f0 --- /dev/null +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/BearerThrottlingSentinelAttribute.cs @@ -0,0 +1,23 @@ +using System.Linq; +using Cuemon.AspNetCore.Mvc.Filters.Throttling; +using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; + +namespace Cuemon.AspNetCore.Mvc.Assets +{ + public class BearerThrottlingSentinelAttribute : ThrottlingSentinelAttribute + { + public BearerThrottlingSentinelAttribute(int rateLimit, double window, TimeUnit windowUnit) : base(rateLimit, window, windowUnit) + { + } + + public override string UniqueContextResolver(HttpContext context) + { + if (context.Request.Headers.TryGetValue(HeaderNames.Authorization, out var authorization)) + { + return authorization.ToString().Split(' ').Last(); + } + return null; + } + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs new file mode 100644 index 000000000..c26b246d5 --- /dev/null +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Cuemon.AspNetCore.Mvc.Assets +{ + [ApiController] + [Route("[controller]")] + public class FakeController : ControllerBase + { + [HttpGet] + [BearerThrottlingSentinel(10, 5, TimeUnit.Seconds)] + public IActionResult Get() + { + return Ok("Unit Test"); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj b/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj index a1120c533..b429c3fa1 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj @@ -1,11 +1,23 @@ - + Cuemon.AspNetCore.Mvc + + + + + + + + + + + Always + \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Throttling/ThrottlingSentinelAttributeTest.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Throttling/ThrottlingSentinelAttributeTest.cs new file mode 100644 index 000000000..eca551128 --- /dev/null +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Throttling/ThrottlingSentinelAttributeTest.cs @@ -0,0 +1,105 @@ +using System; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Cuemon.AspNetCore.Http.Throttling; +using Cuemon.AspNetCore.Mvc.Assets; +using Cuemon.Extensions.AspNetCore.Http.Throttling; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Mvc.Filters.Throttling +{ + public class ThrottlingSentinelAttributeTest : AspNetCoreHostTest + { + private readonly IServiceProvider _provider; + + public ThrottlingSentinelAttributeTest(AspNetCoreHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _provider = hostFixture.ServiceProvider; + } + + [Fact] + public async Task Bearer_ShouldThrottleWhenQuotaIsExceeded() + { + var cache = _provider.GetRequiredService(); + var client = Host.GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", nameof(Bearer_ShouldThrottleWhenQuotaIsExceeded)); + + var te = await Assert.ThrowsAsync(async () => + { + for (var i = 0; i < 15; i++) + { + var result = await client.GetAsync("/fake"); + Assert.Equal(StatusCodes.Status200OK, (int)result.StatusCode); + Assert.Equal("Unit Test", await result.Content.ReadAsStringAsync()); + } + }); + + + var ce = cache[nameof(Bearer_ShouldThrottleWhenQuotaIsExceeded)]; + + Assert.InRange(ce.Total, te.RateLimit, 15); + Assert.Equal(ce.Quota.RateLimit, te.RateLimit); + Assert.Equal(ce.Quota.Window, TimeSpan.FromSeconds(5)); + Assert.Equal(StatusCodes.Status429TooManyRequests, te.StatusCode); + } + + [Fact] + public async Task Bearer_ShouldThrottleAndThenRehydrateAfterWindowHasPassed() + { + var cache = _provider.GetRequiredService(); + var client = Host.GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", nameof(Bearer_ShouldThrottleAndThenRehydrateAfterWindowHasPassed)); + + var te = await Assert.ThrowsAsync(async () => + { + for (var i = 0; i < 15; i++) + { + var result = await client.GetAsync("/fake"); + Assert.Equal(StatusCodes.Status200OK, (int)result.StatusCode); + Assert.Equal("Unit Test", await result.Content.ReadAsStringAsync()); + } + }); + + + var ce = cache[nameof(Bearer_ShouldThrottleAndThenRehydrateAfterWindowHasPassed)]; + + Assert.InRange(ce.Total, te.RateLimit, 15); + Assert.Equal(ce.Quota.RateLimit, te.RateLimit); + Assert.Equal(ce.Quota.Window, TimeSpan.FromSeconds(5)); + Assert.Equal(StatusCodes.Status429TooManyRequests, te.StatusCode); + + await Task.Delay(TimeSpan.FromSeconds(5)); + + for (var i = 0; i < 5; i++) + { + var result = await client.GetAsync("/fake"); + Assert.Equal(StatusCodes.Status200OK, (int)result.StatusCode); + Assert.Equal("Unit Test", await result.Content.ReadAsStringAsync()); + } + + Assert.Equal(5, ce.Total); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddControllers().AddApplicationPart(typeof(FakeController).Assembly); + services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); + services.AddMemoryThrottlingCache(); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + app.UseRouting(); + app.UseEndpoints(routes => + { + routes.MapControllers(); + }); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/SeeOtherResultTest.cs b/test/Cuemon.AspNetCore.Mvc.Tests/SeeOtherResultTest.cs new file mode 100644 index 000000000..bc9a9162f --- /dev/null +++ b/test/Cuemon.AspNetCore.Mvc.Tests/SeeOtherResultTest.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading.Tasks; +using Cuemon.Extensions.Xunit.Hosting; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Mvc +{ + public class SeeOtherResultTest : HostTest + { + private readonly IServiceProvider _provider; + + public SeeOtherResultTest(HostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _provider = hostFixture.ServiceProvider; + } + + [Fact] + public async Task ExecuteResultAsync_ShouldReturnStatusCode303AndLocationUri() + { + var uri = new Uri("https://www.cuemon.net/"); + var context = _provider.GetRequiredService().HttpContext; + var sor = new SeeOtherResult(uri); + var ac = new ActionContext(context, new RouteData(), new ActionDescriptor()); + + Assert.Equal(StatusCodes.Status303SeeOther, sor.StatusCode); + Assert.Equal(uri, sor.Location); + + await sor.ExecuteResultAsync(ac); + + Assert.Equal(sor.StatusCode, context.Response.StatusCode); + Assert.Equal(sor.Location.OriginalString, context.Response.Headers[HeaderNames.Location]); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddFakeHttpContextAccessor(ServiceLifetime.Transient); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/xunit.runner.json b/test/Cuemon.AspNetCore.Mvc.Tests/xunit.runner.json new file mode 100644 index 000000000..34b2fe2cd --- /dev/null +++ b/test/Cuemon.AspNetCore.Mvc.Tests/xunit.runner.json @@ -0,0 +1,3 @@ +{ + "shadowCopy": false +} \ No newline at end of file From cde3b47effe17d6b07a21619fdce088421ea66a6 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 00:55:48 +0200 Subject: [PATCH 320/385] Removed Implements line. --- .../Filters/Diagnostics/FaultDescriptorFilter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorFilter.cs index d077d5f27..a8c965cec 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorFilter.cs @@ -12,7 +12,6 @@ namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics { /// /// A filter that, after an action has faulted, provides developer friendly information about an along with a correct . - /// Implements the /// /// /// From 05b6bf3eac9d2bf008aa140a83caea77805e6963 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 00:56:28 +0200 Subject: [PATCH 321/385] Made attribute abstract as default context-resolver did not make sense. Added abstract method UniqueContextResolver. --- .../Throttling/ThrottlingSentinelAttribute.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelAttribute.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelAttribute.cs index d3c03c833..8a1d73737 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelAttribute.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelAttribute.cs @@ -1,6 +1,6 @@ using System; -using System.Text; using Cuemon.AspNetCore.Http.Throttling; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -11,7 +11,7 @@ namespace Cuemon.AspNetCore.Mvc.Filters.Throttling /// Represents an attribute that is used to mark an action method to be protected by a throttling sentinel. /// /// - public class ThrottlingSentinelAttribute : ActionFilterAttribute, IFilterFactory + public abstract class ThrottlingSentinelAttribute : ActionFilterAttribute, IFilterFactory { /// /// Initializes a new instance of the class. @@ -19,7 +19,7 @@ public class ThrottlingSentinelAttribute : ActionFilterAttribute, IFilterFactory /// The allowed rate from within a given . /// The duration of the window. /// One of the enumeration values that specifies the time unit of . - public ThrottlingSentinelAttribute(int rateLimit, double window, TimeUnit windowUnit) + protected ThrottlingSentinelAttribute(int rateLimit, double window, TimeUnit windowUnit) { var options= new ThrottlingSentinelOptions(); RateLimit = rateLimit; @@ -86,7 +86,7 @@ public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) return new ThrottlingSentinelFilter(Options.Create(new ThrottlingSentinelOptions() { Quota = new ThrottleQuota(RateLimit, Window, WindowUnit), - ContextResolver = context => new StringBuilder().Append(context.Request.Scheme).Append("://").Append(context.Request.Host).Append(context.Request.PathBase).Append(context.Request.Path).ToString().ToLowerInvariant(), + ContextResolver = UniqueContextResolver, UseRetryAfterHeader = UseRetryAfterHeader, RetryAfterHeader = RetryAfterHeader, TooManyRequestsMessage = TooManyRequestsMessage, @@ -96,6 +96,13 @@ public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) }), tc); } + /// + /// Resolves a unique context of the throttling middleware (eg. IP-address, Authorization header, etc.). + /// + /// The to extract a unique context from. + /// A string that uniquely identifies the requester in need of throttling. + public abstract string UniqueContextResolver(HttpContext context); + /// /// Gets a value that indicates if the result of can be reused across requests. /// From 824754ca81b38dd824161d90a7f107c02e424dd4 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 01:55:01 +0200 Subject: [PATCH 322/385] Added unit tests for UserAgentSentinelFilter. --- .../Assets/FakeController.cs | 6 + .../Cuemon.AspNetCore.Mvc.Tests.csproj | 9 +- .../Headers/UserAgentSentinelFilterTest.cs | 177 ++++++++++++++++++ 3 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 test/Cuemon.AspNetCore.Mvc.Tests/Filters/Headers/UserAgentSentinelFilterTest.cs diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs index c26b246d5..21d5d73f6 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs @@ -12,5 +12,11 @@ public IActionResult Get() { return Ok("Unit Test"); } + + [HttpGet("it")] + public IActionResult GetIt() + { + return Ok("Unit Test"); + } } } \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj b/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj index b429c3fa1..7b3269ae1 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj @@ -1,17 +1,12 @@ - - + Cuemon.AspNetCore.Mvc - - - - - + diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Headers/UserAgentSentinelFilterTest.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Headers/UserAgentSentinelFilterTest.cs new file mode 100644 index 000000000..e0791b1a4 --- /dev/null +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Headers/UserAgentSentinelFilterTest.cs @@ -0,0 +1,177 @@ +using System.Linq; +using System.Threading.Tasks; +using Cuemon.AspNetCore.Http.Headers; +using Cuemon.AspNetCore.Mvc.Assets; +using Cuemon.Extensions.Xunit; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Mvc.Filters.Headers +{ + public class UserAgentSentinelFilterTest : Test + { + public UserAgentSentinelFilterTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task OnActionExecutionAsync_ShouldThrowUserAgentException_BadRequest() + { + using (var filter = FilterTestFactory.CreateFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.AddControllers(o => { o.Filters.Add(); }).AddApplicationPart(typeof(FakeController).Assembly); + services.Configure(o => { o.RequireUserAgentHeader = true; }); + })) + { + var options = filter.ServiceProvider.GetRequiredService>(); + var client = filter.Host.GetTestClient(); + + var uae = await Assert.ThrowsAsync(async () => + { + var result = await client.GetAsync("/fake/it"); + }); + + + Assert.Equal(uae.Message, options.Value.BadRequestMessage); + Assert.Equal(uae.StatusCode, StatusCodes.Status400BadRequest); + + Assert.True(options.Value.RequireUserAgentHeader); + } + } + + [Fact] + public async Task OnActionExecutionAsync_ShouldThrowUserAgentException_Forbidden() + { + using (var filter = FilterTestFactory.CreateFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.Configure(o => + { + o.RequireUserAgentHeader = true; + o.ValidateUserAgentHeader = true; + o.AllowedUserAgents.Add("Cuemon-Agent"); + }); + services.AddControllers(o => { o.Filters.Add(); }).AddApplicationPart(typeof(FakeController).Assembly); + })) + { + var options = filter.ServiceProvider.GetRequiredService>(); + var client = filter.Host.GetTestClient(); + client.DefaultRequestHeaders.Add(HeaderNames.UserAgent, "Invalid-Agent"); + + var uae = await Assert.ThrowsAsync(async () => + { + var result = await client.GetAsync("/fake/it"); + }); + + + Assert.Equal(uae.Message, options.Value.ForbiddenMessage); + Assert.Equal(uae.StatusCode, StatusCodes.Status403Forbidden); + + Assert.True(options.Value.RequireUserAgentHeader); + Assert.True(options.Value.ValidateUserAgentHeader); + Assert.True(options.Value.AllowedUserAgents.Any()); + } + } + + [Fact] + public async Task OnActionExecutionAsync_ShouldThrowUserAgentException_BadRequest_BecauseOfUseGenericResponse() + { + using (var filter = FilterTestFactory.CreateFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.Configure(o => + { + o.RequireUserAgentHeader = true; + o.ValidateUserAgentHeader = true; + o.UseGenericResponse = true; + o.AllowedUserAgents.Add("Cuemon-Agent"); + }); + services.AddControllers(o => { o.Filters.Add(); }).AddApplicationPart(typeof(FakeController).Assembly); + })) + { + var options = filter.ServiceProvider.GetRequiredService>(); + var client = filter.Host.GetTestClient(); + client.DefaultRequestHeaders.Add(HeaderNames.UserAgent, "Invalid-Agent"); + + var uae = await Assert.ThrowsAsync(async () => + { + var result = await client.GetAsync("/fake/it"); + }); + + Assert.Equal(uae.Message, options.Value.BadRequestMessage); + Assert.Equal(uae.StatusCode, StatusCodes.Status400BadRequest); + + Assert.True(options.Value.RequireUserAgentHeader); + Assert.True(options.Value.ValidateUserAgentHeader); + Assert.True(options.Value.UseGenericResponse); + Assert.True(options.Value.AllowedUserAgents.Any()); + } + } + + [Fact] + public async Task OnActionExecutionAsync_ShouldAllowRequestUnconditional() + { + using (var filter = FilterTestFactory.CreateFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => services.AddControllers(o => { o.Filters.Add(); }).AddApplicationPart(typeof(FakeController).Assembly))) + { + var options = filter.ServiceProvider.GetRequiredService>(); + + var client = filter.Host.GetTestClient(); + var result = await client.GetAsync("/fake/it"); + + Assert.Equal(StatusCodes.Status200OK, (int) result.StatusCode); + Assert.False(options.Value.RequireUserAgentHeader); + } + } + + [Fact] + public async Task OnActionExecutionAsync_ShouldAllowRequestAfterBeingValidated() + { + using (var filter = FilterTestFactory.CreateFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.Configure(o => + { + o.RequireUserAgentHeader = true; + o.ValidateUserAgentHeader = true; + o.AllowedUserAgents.Add("Cuemon-Agent"); + }); + services.AddControllers(o => { o.Filters.Add(); }).AddApplicationPart(typeof(FakeController).Assembly); + })) + { + var options = filter.ServiceProvider.GetRequiredService>(); + var client = filter.Host.GetTestClient(); + client.DefaultRequestHeaders.Add(HeaderNames.UserAgent, "Cuemon-Agent"); + + var result = await client.GetAsync("/fake/it"); + + Assert.True(options.Value.RequireUserAgentHeader); + Assert.True(options.Value.ValidateUserAgentHeader); + Assert.Equal(StatusCodes.Status200OK, (int) result.StatusCode); + } + } + } +} \ No newline at end of file From 37b40e901f7f2c00da6f8453375cee8bb090436c Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 01:55:39 +0200 Subject: [PATCH 323/385] New interface; IHostTest. --- ...on.Extensions.Xunit.Hosting.AspNetCore.csproj | 4 ++-- .../Cuemon.Extensions.Xunit.Hosting.csproj | 10 +++++----- .../IHostFixture.cs | 8 +------- src/Cuemon.Extensions.Xunit.Hosting/IHostTest.cs | 16 ++++++++++++++++ 4 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/IHostTest.cs diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj index 3d7a695ee..8f8f7bfe4 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -13,7 +13,7 @@ - + diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj index 9921f36d7..7db061918 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj @@ -13,11 +13,11 @@ - - - - - + + + + + diff --git a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs index 61f2370d0..dfd961a18 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs @@ -9,7 +9,7 @@ namespace Cuemon.Extensions.Xunit.Hosting /// Provides a way to use Microsoft Dependency Injection in unit tests. /// /// - public interface IHostFixture : IServiceTest + public interface IHostFixture : IServiceTest, IHostTest { #if NETSTANDARD /// @@ -30,12 +30,6 @@ public interface IHostFixture : IServiceTest /// /// The delegate that adds services to the container. Action ConfigureServicesCallback { get; set; } - - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - IHost Host { get; } /// /// Gets the initialized by the . diff --git a/src/Cuemon.Extensions.Xunit.Hosting/IHostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting/IHostTest.cs new file mode 100644 index 000000000..172aaa516 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/IHostTest.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Extensions.Xunit.Hosting +{ + /// + /// Represents the members needed for ASP.NET Core host testing. + /// + public interface IHostTest + { + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IHost Host { get; } + } +} \ No newline at end of file From 0ab54e779fa6aba5b77928765547a6f104acd531 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 02:00:36 +0200 Subject: [PATCH 324/385] Added new assembly for ASP.NET Core MVC unit testing. --- Cuemon.sln | 9 +++- .../AspNetCoreHostFixtureExtensions.cs | 10 ++++ ...nsions.Xunit.Hosting.AspNetCore.Mvc.csproj | 21 ++++++++ .../IMvcFilterTest.cs | 15 ++++++ .../MvcFilterAspNetCoreHostTest.cs | 50 +++++++++++++++++++ .../MvcFilterTestFactory.cs | 27 ++++++++++ .../Properties/AssemblyInfo.cs | 4 ++ .../Properties/PackageReleaseNotes.txt | 6 +++ .../Headers/UserAgentSentinelFilterTest.cs | 10 ++-- 9 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/AspNetCoreHostFixtureExtensions.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/IMvcFilterTest.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterAspNetCoreHostTest.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterTestFactory.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/AssemblyInfo.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt diff --git a/Cuemon.sln b/Cuemon.sln index 175968961..54adc19d4 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -129,7 +129,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Data.SqlClient.Tests EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore", "src\Cuemon.Extensions.Xunit.Hosting.AspNetCore\Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj", "{200BDF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests", "test\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj", "{72422689-CDC3-4AD6-89D7-25F85545B0FE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests", "test\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj", "{72422689-CDC3-4AD6-89D7-25F85545B0FE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc", "src\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj", "{193C3DBA-DB3A-40D9-A95B-3102189910E5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -385,6 +387,10 @@ Global {72422689-CDC3-4AD6-89D7-25F85545B0FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {72422689-CDC3-4AD6-89D7-25F85545B0FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {72422689-CDC3-4AD6-89D7-25F85545B0FE}.Release|Any CPU.Build.0 = Release|Any CPU + {193C3DBA-DB3A-40D9-A95B-3102189910E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {193C3DBA-DB3A-40D9-A95B-3102189910E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {193C3DBA-DB3A-40D9-A95B-3102189910E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {193C3DBA-DB3A-40D9-A95B-3102189910E5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -452,6 +458,7 @@ Global {A9610C9E-1944-4771-A5E1-CE47ADA243D5} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {200BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} {72422689-CDC3-4AD6-89D7-25F85545B0FE} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} + {193C3DBA-DB3A-40D9-A95B-3102189910E5} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2A848386-B682-4F6D-8254-B5F6247C3054} diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/AspNetCoreHostFixtureExtensions.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/AspNetCoreHostFixtureExtensions.cs new file mode 100644 index 000000000..0362342ce --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/AspNetCoreHostFixtureExtensions.cs @@ -0,0 +1,10 @@ +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc +{ + internal static class AspNetCoreHostFixtureExtensions + { + internal static bool HasValidState(this IAspNetCoreHostFixture fixture) + { + return fixture.ConfigureServicesCallback != null && fixture.Host != null && fixture.ServiceProvider != null && fixture.Application != null; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj new file mode 100644 index 000000000..523a555aa --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj @@ -0,0 +1,21 @@ + + + netcoreapp3.1 + 210bdf91-e7c7-4cb4-a39d-e1a5374c5602 + + + + Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc + Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc + The Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core MVC and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.Mvc.Testing namespace. + asp-net-core-host-test class-fixture asp-net-core-host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services test-server + + + + + + + + + + \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/IMvcFilterTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/IMvcFilterTest.cs new file mode 100644 index 000000000..c5047f646 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/IMvcFilterTest.cs @@ -0,0 +1,15 @@ +using System; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc +{ + /// + /// Represents the members needed for ASP.NET Core MVC filter testing. + /// + /// + /// + /// + /// + public interface IMvcFilterTest : IServiceTest, IPipelineTest, IHostTest, IDisposable + { + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterAspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterAspNetCoreHostTest.cs new file mode 100644 index 000000000..ad1aea612 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterAspNetCoreHostTest.cs @@ -0,0 +1,50 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc +{ + internal class MvcFilterAspNetCoreHostTest : AspNetCoreHostTest, IMvcFilterTest + { + private readonly Action _pipelineConfigurator; + private readonly Action _serviceConfigurator; + + internal MvcFilterAspNetCoreHostTest(Action pipelineConfigurator, Action serviceConfigurator, AspNetCoreHostFixture hostFixture) : base(hostFixture) + { + _pipelineConfigurator = pipelineConfigurator; + _serviceConfigurator = serviceConfigurator; + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureCallback = Configure; + hostFixture.ConfigureServicesCallback = ConfigureServices; + hostFixture.ConfigureApplicationCallback = ConfigureApplication; + hostFixture.ConfigureHost(this); + } + Host = hostFixture.Host; + ServiceProvider = hostFixture.Host.Services; + Application = hostFixture.Application; + } + + protected override void InitializeHostFixture(AspNetCoreHostFixture hostFixture) + { + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + _pipelineConfigurator(app); + } + + public override void ConfigureServices(IServiceCollection services) + { + _serviceConfigurator(services); + } + + /// + /// Called when this object is being disposed by either or having disposing set to true and is false. + /// + protected override void OnDisposeManagedResources() + { + Host?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterTestFactory.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterTestFactory.cs new file mode 100644 index 000000000..6fb1a29c7 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterTestFactory.cs @@ -0,0 +1,27 @@ +using System; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc +{ + /// + /// Provides a set of static methods for ASP.NET Core MVC filter unit testing. + /// + public static class MvcFilterTestFactory + { + /// + /// Creates and returns an implementation. + /// + /// The which may be configured. + /// The which may be configured. + /// An instance of an implementation. + public static IMvcFilterTest CreateMvcFilterTest(Action pipelineSetup = null, Action serviceSetup = null) + { + pipelineSetup ??= app => app.UseFakeHttpResponseTrigger(); + serviceSetup ??= services => services.AddScoped(); + return new MvcFilterAspNetCoreHostTest(pipelineSetup, serviceSetup, new AspNetCoreHostFixture()); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/AssemblyInfo.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..ac11a1b83 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("66569727-6792-48ee-84fe-8cc685772716")] \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..45c106b06 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt @@ -0,0 +1,6 @@ +Version: 6.0.0 +Availability: NET Core 3.1 +  +# New Features +- ADDED IMvcFilterTest interface in the Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc namespace that represents the members needed for ASP.NET Core MVC filter testing +- ADDED MvcFilterTestFactory class in the Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc namespace that provides a set of static methods for ASP.NET Core MVC filter unit testing \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Headers/UserAgentSentinelFilterTest.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Headers/UserAgentSentinelFilterTest.cs index e0791b1a4..04d0f0b5a 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Headers/UserAgentSentinelFilterTest.cs +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Headers/UserAgentSentinelFilterTest.cs @@ -24,7 +24,7 @@ public UserAgentSentinelFilterTest(ITestOutputHelper output) : base(output) [Fact] public async Task OnActionExecutionAsync_ShouldThrowUserAgentException_BadRequest() { - using (var filter = FilterTestFactory.CreateFilterTest(app => + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => { app.UseRouting(); app.UseEndpoints(routes => { routes.MapControllers(); }); @@ -53,7 +53,7 @@ public async Task OnActionExecutionAsync_ShouldThrowUserAgentException_BadReques [Fact] public async Task OnActionExecutionAsync_ShouldThrowUserAgentException_Forbidden() { - using (var filter = FilterTestFactory.CreateFilterTest(app => + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => { app.UseRouting(); app.UseEndpoints(routes => { routes.MapControllers(); }); @@ -90,7 +90,7 @@ public async Task OnActionExecutionAsync_ShouldThrowUserAgentException_Forbidden [Fact] public async Task OnActionExecutionAsync_ShouldThrowUserAgentException_BadRequest_BecauseOfUseGenericResponse() { - using (var filter = FilterTestFactory.CreateFilterTest(app => + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => { app.UseRouting(); app.UseEndpoints(routes => { routes.MapControllers(); }); @@ -128,7 +128,7 @@ public async Task OnActionExecutionAsync_ShouldThrowUserAgentException_BadReques [Fact] public async Task OnActionExecutionAsync_ShouldAllowRequestUnconditional() { - using (var filter = FilterTestFactory.CreateFilterTest(app => + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => { app.UseRouting(); app.UseEndpoints(routes => { routes.MapControllers(); }); @@ -147,7 +147,7 @@ public async Task OnActionExecutionAsync_ShouldAllowRequestUnconditional() [Fact] public async Task OnActionExecutionAsync_ShouldAllowRequestAfterBeingValidated() { - using (var filter = FilterTestFactory.CreateFilterTest(app => + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => { app.UseRouting(); app.UseEndpoints(routes => { routes.MapControllers(); }); From 5dd2d3556e21203f407807d31a1029be34bf198d Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 02:01:12 +0200 Subject: [PATCH 325/385] Fixed NuGet reference and ported consequence change. --- .../Assets/UserSecretsHostFixture.cs | 1 - .../Cuemon.Data.SqlClient.Tests.csproj | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs b/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs index 22647b1cb..fbe622864 100644 --- a/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs +++ b/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs @@ -32,7 +32,6 @@ public override void ConfigureHost(Test hostTest) Configuration = context.Configuration; HostingEnvironment = context.HostingEnvironment; ConfigureServicesCallback(services); - ServiceProvider = services.BuildServiceProvider(); }).Build(); } } diff --git a/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj b/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj index 66f841458..ce585ddc6 100644 --- a/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj +++ b/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj @@ -6,7 +6,7 @@ - + From 0e4f6d054561b5a057afbd5044c2294183931d61 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 02:06:20 +0200 Subject: [PATCH 326/385] Updated package, tag and release notes for package. Included new assembly in CI-build. --- azure-pipelines.yml | 1 + ....Extensions.Xunit.Hosting.AspNetCore.Mvc.md | 18 ++++++++++++++++++ ...emon.Extensions.Xunit.Hosting.AspNetCore.md | 2 +- ...ensions.Xunit.Hosting.AspNetCore.Mvc.csproj | 2 +- ....Extensions.Xunit.Hosting.AspNetCore.csproj | 2 +- 5 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.md diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0ad62dd6f..2534721ed 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -109,6 +109,7 @@ jobs: command: 'build' projects: | src/**/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj + src/**/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj arguments: '--configuration $(BuildConfiguration) --no-restore --framework netcoreapp3.1' workingDirectory: '$(BuildSource)' diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.md new file mode 100644 index 000000000..060820a2a --- /dev/null +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.md @@ -0,0 +1,18 @@ +--- +uid: Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc +summary: *content +--- +The Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core MVC and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.Mvc.Testing namespace. + +Availability: NET Core 3.1 + +Complements: [Microsoft.AspNetCore.Mvc.Testing namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.testing?view=aspnetcore-3.0) 🔗 + +Github branches: 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc)\ +[Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md index 78f807bf3..09718b8f1 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md @@ -2,7 +2,7 @@ uid: Cuemon.Extensions.Xunit.Hosting.AspNetCore summary: *content --- -The Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.TestHost namespace +The Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.TestHost namespace. Availability: NET Core 3.1 diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj index 523a555aa..f5e76ce3f 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj @@ -8,7 +8,7 @@ Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc The Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core MVC and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.Mvc.Testing namespace. - asp-net-core-host-test class-fixture asp-net-core-host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services test-server + i-mvc-filter-test mvc-filter-test-factory microsoft dependency injection host configuration hosting-environment service-provider configure-services test-server diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj index 8f8f7bfe4..70a403017 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj @@ -9,7 +9,7 @@ Cuemon.Extensions.Xunit.Hosting.AspNetCore Cuemon.Extensions.Xunit.Hosting.AspNetCore The Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.TestHost namespace. - asp-net-core-host-test class-fixture asp-net-core-host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services test-server + asp-net-core-host-test class-fixture asp-net-core-host-fixture middleware-test-factory microsoft dependency injection host configuration hosting-environment service-provider configure-services test-server From ee6f216c58debe74820d5a0ddbb79f30f697e35a Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 18:44:35 +0200 Subject: [PATCH 327/385] Refactored TimeMeasuring* to only support ServerTiming. --- ...eAttribute.cs => ServerTimingAttribute.cs} | 42 +++++--- ...asuringFilter.cs => ServerTimingFilter.cs} | 27 +++-- .../Diagnostics/ServerTimingOptions.cs | 48 +++++++++ .../Diagnostics/TimeMeasuringOptions.cs | 77 -------------- .../Cuemon.AspNetCore.csproj | 4 - .../Diagnostics/IServerTiming.cs | 41 +++++++ .../Diagnostics/ServerTiming.cs | 68 ++++++++++++ .../Diagnostics/ServerTimingMetric.cs | 62 +++++++++++ .../ServiceCollectionExtensions.cs | 34 ++++++ .../IMvcFilterTest.cs | 4 +- .../IMiddlewareTest.cs | 4 +- .../IConfigurationTest.cs | 17 +++ .../IHostFixture.cs | 22 +--- .../IHostingEnvironmentTest.cs | 24 +++++ .../Diagnostics/TimeMeasuringFilterTest.cs | 100 ++++++++++++++++++ 15 files changed, 446 insertions(+), 128 deletions(-) rename src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/{TimeMeasureAttribute.cs => ServerTimingAttribute.cs} (62%) rename src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/{TimeMeasuringFilter.cs => ServerTimingFilter.cs} (74%) create mode 100644 src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingOptions.cs delete mode 100644 src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasuringOptions.cs create mode 100644 src/Cuemon.AspNetCore/Diagnostics/IServerTiming.cs create mode 100644 src/Cuemon.AspNetCore/Diagnostics/ServerTiming.cs create mode 100644 src/Cuemon.AspNetCore/Diagnostics/ServerTimingMetric.cs create mode 100644 src/Cuemon.Extensions.AspNetCore/Diagnostics/ServiceCollectionExtensions.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/IConfigurationTest.cs create mode 100644 src/Cuemon.Extensions.Xunit.Hosting/IHostingEnvironmentTest.cs create mode 100644 test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/TimeMeasuringFilterTest.cs diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasureAttribute.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingAttribute.cs similarity index 62% rename from src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasureAttribute.cs rename to src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingAttribute.cs index 7dd9af602..463acad24 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasureAttribute.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingAttribute.cs @@ -10,37 +10,38 @@ namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics /// Represents an attribute that is used to mark an action method for time measure profiling. /// /// - public class TimeMeasureAttribute : ActionFilterAttribute, IFilterFactory + public class ServerTimingAttribute : ActionFilterAttribute, IFilterFactory { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public TimeMeasureAttribute() : this(0, TimeUnit.Ticks) + public ServerTimingAttribute() { } /// - /// Initializes a new instance of the class. + /// Gets or sets the server-specified metric name. /// - /// The value that in combination with specifies the threshold of the action method. - /// One of the enumeration values that specifies the time unit of . - public TimeMeasureAttribute(double threshold, TimeUnit thresholdTimeUnit) - { - Threshold = threshold; - ThresholdTimeUnit = thresholdTimeUnit; - } + /// The server-specified metric name. + public string Name { get; set; } + + /// + /// Gets or sets the server-specified metric description. + /// + /// The server-specified metric description. + public string Description { get; set; } /// /// Gets or sets the value that in combination with specifies the threshold of the action method. /// /// The threshold value of the action method. - public double Threshold { get; set; } + public double Threshold { get; set; } = 0; /// /// Gets or sets one of the enumeration values that specifies the time unit of . /// /// The that defines the actual . - public TimeUnit ThresholdTimeUnit { get; set; } + public TimeUnit ThresholdTimeUnit { get; set; } = TimeUnit.Ticks; /// /// Creates an instance of the executable filter. @@ -54,10 +55,21 @@ public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) #elif NETCOREAPP var he = serviceProvider.GetRequiredService(); #endif - return new TimeMeasuringFilter(Options.Create(new TimeMeasuringOptions() + var filter = new ServerTimingFilter(Options.Create(new ServerTimingOptions() { TimeMeasureCompletedThreshold = Decorator.Enclose(Threshold).ToTimeSpan(ThresholdTimeUnit) - }), he); + }), he) + { + Name = Name, + Description = Description + }; + var stOptions = serviceProvider.GetService>(); + if (stOptions?.Value?.SuppressHeaderPredicate != null) + { + filter.Options.SuppressHeaderPredicate = stOptions.Value.SuppressHeaderPredicate; + } + + return filter; } /// diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasuringFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingFilter.cs similarity index 74% rename from src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasuringFilter.cs rename to src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingFilter.cs index 2ce81e3b3..9aad63b0e 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasuringFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingFilter.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; -using Cuemon.AspNetCore.Http.Headers; +using Cuemon.AspNetCore.Diagnostics; using Cuemon.Diagnostics; using Cuemon.Reflection; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; @@ -17,26 +18,26 @@ namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics /// /// /// - public class TimeMeasuringFilter : ConfigurableActionFilter + public class ServerTimingFilter : ConfigurableActionFilter { #if NETSTANDARD /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The which need to be configured. /// The dependency injected . - public TimeMeasuringFilter(IOptions setup, IHostingEnvironment he) : base(setup) + public ServerTimingFilter(IOptions setup, IHostingEnvironment he) : base(setup) { Profiler = new TimeMeasureProfiler(); Environment = he; } #elif NETCOREAPP /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The which need to be configured. /// The dependency injected . - public TimeMeasuringFilter(IOptions setup, IHostEnvironment he) : base(setup) + public ServerTimingFilter(IOptions setup, IHostEnvironment he) : base(setup) { Profiler = new TimeMeasureProfiler(); Environment = he; @@ -51,6 +52,10 @@ public TimeMeasuringFilter(IOptions setup, IHostEnvironmen private TimeMeasureProfiler Profiler { get; } + internal string Name { get; set; } + + internal string Description { get; set; } + /// /// Called before the action executes, after model binding is complete. /// @@ -76,13 +81,17 @@ public override void OnActionExecuting(ActionExecutingContext context) public override void OnActionExecuted(ActionExecutedContext context) { Profiler.Timer.Stop(); + var serverTiming = context.HttpContext.RequestServices.GetRequiredService(); + serverTiming.AddServerTiming(Name ?? "mvc", Profiler.Elapsed, Description ?? $"[{Decorator.Enclose(Profiler.Member.MethodName).ToAsciiEncodedString()}@{Decorator.Enclose(Profiler.Member.Caller.Name).ToAsciiEncodedString()}]({context.HttpContext.Request.GetEncodedUrl().ToLowerInvariant()})"); if (Options.TimeMeasureCompletedThreshold == TimeSpan.Zero || Profiler.Elapsed > Options.TimeMeasureCompletedThreshold) { TimeMeasure.CompletedCallback?.Invoke(Profiler); if (!Options.SuppressHeaderPredicate(Environment)) { - if (Options.UseServerTimingHeader) { Decorator.Enclose(context.HttpContext.Response.Headers).AddOrUpdateHeader("Server-Timing", FormattableString.Invariant($"CPU;dur={Profiler.Elapsed.TotalMilliseconds.ToString("N1", CultureInfo.InvariantCulture)}")); } - if (Options.UseCustomHeader) { Decorator.Enclose(context.HttpContext.Response.Headers).AddOrUpdateHeader(Options.HeaderName, Profiler.ToString()); } + foreach (var metric in serverTiming.Metrics) + { + context.HttpContext.Response.Headers.Add(ServerTiming.HeaderName, metric.ToString()); + } } } } diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingOptions.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingOptions.cs new file mode 100644 index 000000000..e6a2afc95 --- /dev/null +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingOptions.cs @@ -0,0 +1,48 @@ +using System; +using Cuemon.Diagnostics; +using Microsoft.Extensions.Hosting; + +namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics +{ + /// + /// Configuration options for . + /// + /// + public class ServerTimingOptions : TimeMeasureOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// + /// + /// + /// + /// he => he.IsProduction() + /// + /// + /// + public ServerTimingOptions() + { + SuppressHeaderPredicate = he => he.IsProduction(); + } + + /// + /// Gets or sets the predicate that can suppress the Server-Timing HTTP header(s). + /// + /// The function delegate that can determine if the Server-Timing HTTP header(s) should be suppressed. + #if NETSTANDARD + public Func SuppressHeaderPredicate { get; set; } + #elif NETCOREAPP + public Func SuppressHeaderPredicate { get; set; } + #endif + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasuringOptions.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasuringOptions.cs deleted file mode 100644 index 20cbb0c5e..000000000 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/TimeMeasuringOptions.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using Cuemon.Diagnostics; -using Microsoft.Extensions.Hosting; - -namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics -{ - /// - /// Configuration options for . - /// - /// - public class TimeMeasuringOptions : TimeMeasureOptions - { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// - /// - /// - /// - /// X-Action-Profiler - /// - /// - /// - /// _ => false - /// - /// - /// - /// true - /// - /// - /// - public TimeMeasuringOptions() - { - HeaderName = "X-Action-Profiler"; - SuppressHeaderPredicate = _ => false; - UseServerTimingHeader = true; - UseCustomHeader = true; - } - - /// - /// Gets or sets the name of the custom time-measured HTTP header. - /// - /// The name of the custom time-measured HTTP header. - public string HeaderName { get; set; } - - /// - /// Gets or sets the predicate that can suppress either of the time-measured HTTP headers. - /// - /// The function delegate that can determine if either of the time-measured HTTP headers should be suppressed. - #if NETSTANDARD - public Func SuppressHeaderPredicate { get; set; } - #elif NETCOREAPP - public Func SuppressHeaderPredicate { get; set; } - #endif - - /// - /// Gets or sets a value indicating whether to include a Server-Timing HTTP header specifying how long an action took to execute. - /// - /// true to include a Server-Timing HTTP header specifying how long an action took to execute; otherwise, false. - public bool UseServerTimingHeader { get; set; } - - /// - /// Gets or sets a value indicating whether to include the custom specifying how long an action took to execute. - /// - /// true to include the custom specifying how long an action took to execute; otherwise, false. - public bool UseCustomHeader { get; set; } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj index c7a90c06a..68ad01a70 100644 --- a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj +++ b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj @@ -31,8 +31,4 @@ - - - - \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Diagnostics/IServerTiming.cs b/src/Cuemon.AspNetCore/Diagnostics/IServerTiming.cs new file mode 100644 index 000000000..a0be43623 --- /dev/null +++ b/src/Cuemon.AspNetCore/Diagnostics/IServerTiming.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; + +namespace Cuemon.AspNetCore.Diagnostics +{ + /// + /// Represents the Server Timing as per W3C Working Draft 28 July 2020 (https://www.w3.org/TR/2020/WD-server-timing-20200728/). + /// + public interface IServerTiming + { + /// + /// Adds a to the . + /// + /// The server-specified metric name. + /// A reference to this instance after the operation has completed. + IServerTiming AddServerTiming(string name); + + /// + /// Adds a to the . + /// + /// The server-specified metric name. + /// The server-specified metric duration. + /// A reference to this instance after the operation has completed. + IServerTiming AddServerTiming(string name, TimeSpan duration); + + /// + /// Adds a to the . + /// + /// The server-specified metric name. + /// The server-specified metric duration. + /// The server-specified metric description. + /// A reference to this instance after the operation has completed. + IServerTiming AddServerTiming(string name, TimeSpan duration, string description); + + /// + /// Gets the entries used to communicate one or more metrics and descriptions for the given request-response cycle. + /// + /// The entries used to communicate one or more metrics and descriptions for the given request-response cycle. + IEnumerable Metrics { get; } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Diagnostics/ServerTiming.cs b/src/Cuemon.AspNetCore/Diagnostics/ServerTiming.cs new file mode 100644 index 000000000..7fbe6fee7 --- /dev/null +++ b/src/Cuemon.AspNetCore/Diagnostics/ServerTiming.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; + +namespace Cuemon.AspNetCore.Diagnostics +{ + /// + /// Provides a default implementation of the interface. + /// + /// + public class ServerTiming : IServerTiming + { + /// + /// The name of the Server-Timing header field. + /// + public const string HeaderName = "Server-Timing"; + + private readonly List _metrics = new List(); + + /// + /// Initializes a new instance of the class. + /// + public ServerTiming() + { + } + + /// + /// Adds a to the . + /// + /// The server-specified metric name. + /// A reference to this instance after the operation has completed. + public IServerTiming AddServerTiming(string name) + { + _metrics.Add(new ServerTimingMetric(name)); + return this; + } + + /// + /// Adds a to the . + /// + /// The server-specified metric name. + /// The server-specified metric duration. + /// A reference to this instance after the operation has completed. + public IServerTiming AddServerTiming(string name, TimeSpan duration) + { + _metrics.Add(new ServerTimingMetric(name, duration)); + return this; + } + + /// + /// Adds a to the . + /// + /// The server-specified metric name. + /// The server-specified metric duration. + /// The server-specified metric description. + /// A reference to this instance after the operation has completed. + public IServerTiming AddServerTiming(string name, TimeSpan duration, string description) + { + _metrics.Add(new ServerTimingMetric(name, duration, description)); + return this; + } + + /// + /// Gets the entries used to communicate one or more metrics and descriptions for the given request-response cycle. + /// + /// The entries used to communicate one or more metrics and descriptions for the given request-response cycle. + public IEnumerable Metrics => _metrics; + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Diagnostics/ServerTimingMetric.cs b/src/Cuemon.AspNetCore/Diagnostics/ServerTimingMetric.cs new file mode 100644 index 000000000..95cef4c3d --- /dev/null +++ b/src/Cuemon.AspNetCore/Diagnostics/ServerTimingMetric.cs @@ -0,0 +1,62 @@ +using System; +using System.Globalization; + +namespace Cuemon.AspNetCore.Diagnostics +{ + /// + /// Represents a HTTP Server-Timing header field entry to communicate one metric and description for the given request-response cycle. + /// + public class ServerTimingMetric + { + private readonly string _metric; + + /// + /// Initializes a new instance of the class. + /// + /// The server-specified metric name. + /// The server-specified metric duration. + /// The server-specified metric description. + public ServerTimingMetric(string name, TimeSpan? duration = null, string description = null) + { + Validator.ThrowIfNullOrWhitespace(name, nameof(name)); + if (duration.HasValue && duration <= TimeSpan.Zero) { duration = TimeSpan.Zero; } + + var metric = name; + if (duration.HasValue) { metric = string.Concat(metric, ";", FormattableString.Invariant($"dur={duration.Value.TotalMilliseconds.ToString("N1", CultureInfo.InvariantCulture)}")); } + if (description != null) { metric = string.Concat(metric, ";", $"desc=\"{description}\""); } + + Name = name; + Duration = duration; + Description = description; + + _metric = metric; + } + + /// + /// Gets the server-specified metric name. + /// + /// The server-specified metric name. + public string Name { get; } + + /// + /// Gets the server-specified metric duration. + /// + /// The server-specified metric duration. + public TimeSpan? Duration { get; } + + /// + /// Gets the server-specified metric description. + /// + /// The server-specified metric description. + public string Description { get; } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + return _metric; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore/Diagnostics/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Diagnostics/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..a2b3d450f --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore/Diagnostics/ServiceCollectionExtensions.cs @@ -0,0 +1,34 @@ +using Cuemon.AspNetCore.Diagnostics; +using Cuemon.Extensions.AspNetCore.Http.Throttling; +using Microsoft.Extensions.DependencyInjection; + +namespace Cuemon.Extensions.AspNetCore.Diagnostics +{ + /// + /// Extension methods for the interface. + /// + public static class ServiceCollectionExtensions + { + /// + /// Adds a service to the specified . + /// + /// The to add services to. + /// An that can be used to further configure other services. + public static IServiceCollection AddServerTiming(this IServiceCollection services) + { + return services.AddServerTiming(); + } + + /// + /// Adds a throttling cache service to the specified . + /// + /// The to add services to. + /// An that can be used to further configure other services. + public static IServiceCollection AddServerTiming(this IServiceCollection services) where T : class, IServerTiming + { + Validator.ThrowIfNull(services, nameof(services)); + services.AddScoped(); + return services; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/IMvcFilterTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/IMvcFilterTest.cs index c5047f646..e96a9821a 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/IMvcFilterTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/IMvcFilterTest.cs @@ -8,8 +8,10 @@ namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc /// /// /// + /// + /// /// - public interface IMvcFilterTest : IServiceTest, IPipelineTest, IHostTest, IDisposable + public interface IMvcFilterTest : IServiceTest, IPipelineTest, IHostTest, IConfigurationTest, IHostingEnvironmentTest, IDisposable { } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IMiddlewareTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IMiddlewareTest.cs index c4bb273d8..bc8d91203 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IMiddlewareTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/IMiddlewareTest.cs @@ -7,8 +7,10 @@ namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore /// /// /// + /// + /// /// - public interface IMiddlewareTest : IServiceTest, IPipelineTest, IDisposable + public interface IMiddlewareTest : IServiceTest, IPipelineTest, IConfigurationTest, IHostingEnvironmentTest, IDisposable { } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting/IConfigurationTest.cs b/src/Cuemon.Extensions.Xunit.Hosting/IConfigurationTest.cs new file mode 100644 index 000000000..246ff3ddc --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/IConfigurationTest.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Extensions.Xunit.Hosting +{ + /// + /// Represents the members needed for ASP.NET Core testing with support for Configuration. + /// + public interface IConfigurationTest + { + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IConfiguration Configuration { get; } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs index dfd961a18..a5f4d3318 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting/IHostFixture.cs @@ -9,7 +9,7 @@ namespace Cuemon.Extensions.Xunit.Hosting /// Provides a way to use Microsoft Dependency Injection in unit tests. /// /// - public interface IHostFixture : IServiceTest, IHostTest + public interface IHostFixture : IServiceTest, IHostTest, IConfigurationTest, IHostingEnvironmentTest { #if NETSTANDARD /// @@ -31,26 +31,6 @@ public interface IHostFixture : IServiceTest, IHostTest /// The delegate that adds services to the container. Action ConfigureServicesCallback { get; set; } - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - IConfiguration Configuration { get; } - - #if NETSTANDARD - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - IHostingEnvironment HostingEnvironment { get; } - #elif NETCOREAPP - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - IHostEnvironment HostingEnvironment { get; } - #endif - /// /// Creates and configures the of this . /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting/IHostingEnvironmentTest.cs b/src/Cuemon.Extensions.Xunit.Hosting/IHostingEnvironmentTest.cs new file mode 100644 index 000000000..9106d18ac --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting/IHostingEnvironmentTest.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.Hosting; + +namespace Cuemon.Extensions.Xunit.Hosting +{ + /// + /// Represents the members needed for ASP.NET Core testing with support for HostingEnvironment. + /// + public interface IHostingEnvironmentTest + { + #if NETSTANDARD + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IHostingEnvironment HostingEnvironment { get; } + #elif NETCOREAPP + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IHostEnvironment HostingEnvironment { get; } + #endif + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/TimeMeasuringFilterTest.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/TimeMeasuringFilterTest.cs new file mode 100644 index 000000000..65af147e7 --- /dev/null +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/TimeMeasuringFilterTest.cs @@ -0,0 +1,100 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Cuemon.AspNetCore.Diagnostics; +using Cuemon.AspNetCore.Mvc.Assets; +using Cuemon.Diagnostics; +using Cuemon.Extensions.AspNetCore.Diagnostics; +using Cuemon.Extensions.Xunit; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics +{ + public class TimeMeasuringFilterTest : Test + { + public TimeMeasuringFilterTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task OnActionExecutionAsync_ShouldTimeMeasureFakeController() + { + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.AddServerTiming(); + services.AddControllers(o => { o.Filters.Add(); }).AddApplicationPart(typeof(FakeController).Assembly); + })) + { + var client = filter.Host.GetTestClient(); + var profiler = await TimeMeasure.WithFuncAsync(client.GetAsync, "/fake/oneSecond"); + + Assert.InRange(profiler.Elapsed, TimeSpan.Zero, TimeSpan.FromSeconds(5)); + Assert.True(profiler.Result.Headers.Contains("Server-Timing")); + Assert.StartsWith("mvc", profiler.Result.Headers.GetValues(ServerTiming.HeaderName).Single()); + + TestOutput.WriteLine(profiler.Elapsed.ToString()); + } + } + + [Fact] + public async Task ServerTimingAttribute_ShouldTimeMeasureFakeController_GetAfter1SecondDecorated() + { + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.AddServerTiming(); + services.AddControllers().AddApplicationPart(typeof(FakeController).Assembly); + })) + { + var client = filter.Host.GetTestClient(); + var profiler = await TimeMeasure.WithFuncAsync(client.GetAsync, "/fake/oneSecondAttribute"); + + Assert.InRange(profiler.Elapsed, TimeSpan.Zero, TimeSpan.FromSeconds(5)); + Assert.True(profiler.Result.Headers.Contains("Server-Timing")); + Assert.StartsWith("action-result", profiler.Result.Headers.GetValues(ServerTiming.HeaderName).Single()); + + TestOutput.WriteLine(profiler.Elapsed.ToString()); + } + } + + [Fact] + public async Task ServerTimingAttribute_ShouldSuppressTimeMeasureFakeController_GetAfter1SecondDecorated() + { + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.AddServerTiming(); + services.AddControllers().AddApplicationPart(typeof(FakeController).Assembly); + services.Configure(o => o.SuppressHeaderPredicate = _ => true); + })) + { + var options = filter.ServiceProvider.GetRequiredService>(); + var client = filter.Host.GetTestClient(); + + var profiler = await TimeMeasure.WithFuncAsync(client.GetAsync, "/fake/oneSecondAttribute"); + + Assert.InRange(profiler.Elapsed, TimeSpan.Zero, TimeSpan.FromSeconds(5)); + Assert.True(options.Value.SuppressHeaderPredicate(filter.HostingEnvironment)); + Assert.False(profiler.Result.Headers.Contains("Server-Timing")); + + TestOutput.WriteLine(profiler.Elapsed.ToString()); + } + } + } +} \ No newline at end of file From 791a1ac8d6215d9f54fca5d95f6cf7847ae3ff1b Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 18:45:17 +0200 Subject: [PATCH 328/385] Had to delay due to random error on ADO. --- .../Http/Throttling/ThrottlingSentinelMiddlewareTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs index 73ed16f7a..a557407bf 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs @@ -96,7 +96,7 @@ public async Task InvokeAsync_ShouldRehydrate() TestOutput.WriteLine(te.Delta.ToString()); - await Task.Delay(window); + await Task.Delay(te.Delta.Add(TimeSpan.FromSeconds(1))); await pipeline(context); From 920e7ed0712e7bbf1d9ab078d6773081ff1b3462 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 18:45:48 +0200 Subject: [PATCH 329/385] Extended controller. --- .../Assets/FakeController.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs index 21d5d73f6..5ab31a3fe 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs @@ -1,4 +1,7 @@ -using Microsoft.AspNetCore.Mvc; +using System; +using System.Threading.Tasks; +using Cuemon.AspNetCore.Mvc.Filters.Diagnostics; +using Microsoft.AspNetCore.Mvc; namespace Cuemon.AspNetCore.Mvc.Assets { @@ -18,5 +21,20 @@ public IActionResult GetIt() { return Ok("Unit Test"); } + + [HttpGet("oneSecond")] + public async Task GetAfter1Second() + { + await Task.Delay(TimeSpan.FromSeconds(1)); + return Ok("Unit Test"); + } + + [ServerTiming(Name = "action-result")] + [HttpGet("oneSecondAttribute")] + public async Task GetAfter1SecondDecorated() + { + await Task.Delay(TimeSpan.FromSeconds(1)); + return Ok("Unit Test"); + } } } \ No newline at end of file From 39ae1834c8b7b7ee99ef53e54e9b7d668f3765d4 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 18:46:23 +0200 Subject: [PATCH 330/385] Added rest of Xunit assembly family. --- docfx/docfx.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docfx/docfx.json b/docfx/docfx.json index ff114a353..ac6b4f2ef 100644 --- a/docfx/docfx.json +++ b/docfx/docfx.json @@ -45,7 +45,10 @@ "Cuemon.Extensions.Text/**.csproj", "Cuemon.Extensions.Threading/**.csproj", "Cuemon.Extensions.Xml/**.csproj", - "Cuemon.Extensions.Xunit/**.csproj" + "Cuemon.Extensions.Xunit/**.csproj", + "Cuemon.Extensions.Xunit.Hosting/**.csproj", + "Cuemon.Extensions.Xunit.Hosting.AspNetCore/**.csproj", + "Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/**.csproj" ], "src": "../src" } From 931db4ac2e05c607cd9fa62380e656a76b647fc4 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 19:55:21 +0200 Subject: [PATCH 331/385] Fixed to have only one header with multiple values. Adjusted tests. Also testetd with browsers. --- .../Filters/Diagnostics/ServerTimingFilter.cs | 6 +-- .../Diagnostics/ServerTimingMetric.cs | 2 +- ...ilterTest.cs => ServerTimingFilterTest.cs} | 43 ++++++++++++++++++- 3 files changed, 44 insertions(+), 7 deletions(-) rename test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/{TimeMeasuringFilterTest.cs => ServerTimingFilterTest.cs} (66%) diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingFilter.cs index 9aad63b0e..3f1064be2 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingFilter.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics { @@ -88,10 +89,7 @@ public override void OnActionExecuted(ActionExecutedContext context) TimeMeasure.CompletedCallback?.Invoke(Profiler); if (!Options.SuppressHeaderPredicate(Environment)) { - foreach (var metric in serverTiming.Metrics) - { - context.HttpContext.Response.Headers.Add(ServerTiming.HeaderName, metric.ToString()); - } + context.HttpContext.Response.Headers.Add(ServerTiming.HeaderName, serverTiming.Metrics.Select(metric => metric.ToString()).ToArray()); } } } diff --git a/src/Cuemon.AspNetCore/Diagnostics/ServerTimingMetric.cs b/src/Cuemon.AspNetCore/Diagnostics/ServerTimingMetric.cs index 95cef4c3d..3689185e4 100644 --- a/src/Cuemon.AspNetCore/Diagnostics/ServerTimingMetric.cs +++ b/src/Cuemon.AspNetCore/Diagnostics/ServerTimingMetric.cs @@ -22,7 +22,7 @@ public ServerTimingMetric(string name, TimeSpan? duration = null, string descrip if (duration.HasValue && duration <= TimeSpan.Zero) { duration = TimeSpan.Zero; } var metric = name; - if (duration.HasValue) { metric = string.Concat(metric, ";", FormattableString.Invariant($"dur={duration.Value.TotalMilliseconds.ToString("N1", CultureInfo.InvariantCulture)}")); } + if (duration.HasValue) { metric = string.Concat(metric, ";", FormattableString.Invariant($"dur={duration.Value.TotalMilliseconds.ToString("F1", CultureInfo.InvariantCulture)}")); } if (description != null) { metric = string.Concat(metric, ";", $"desc=\"{description}\""); } Name = name; diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/TimeMeasuringFilterTest.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/ServerTimingFilterTest.cs similarity index 66% rename from test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/TimeMeasuringFilterTest.cs rename to test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/ServerTimingFilterTest.cs index 65af147e7..3aa9bbd85 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/TimeMeasuringFilterTest.cs +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/ServerTimingFilterTest.cs @@ -16,12 +16,51 @@ namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics { - public class TimeMeasuringFilterTest : Test + public class ServerTimingFilterTest : Test { - public TimeMeasuringFilterTest(ITestOutputHelper output) : base(output) + public ServerTimingFilterTest(ITestOutputHelper output) : base(output) { } + [Fact] + public async Task OnActionExecutionAsync_ShouldTimeMeasureFakeControllerAndFictiveMeasurements() + { + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => + { + app.UseRouting(); + app.Use(async (context, next) => + { + var serverTiming = context.RequestServices.GetRequiredService(); + + await Task.Delay(22); + serverTiming.AddServerTiming("redis", TimeSpan.FromMilliseconds(22), "Redis Cache"); + + await Task.Delay(1700); + serverTiming.AddServerTiming("restApi", TimeSpan.FromSeconds(1.7), "Some REST API integration"); + + await next(); + }); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.AddServerTiming(); + services.AddControllers(o => { o.Filters.Add(); }).AddApplicationPart(typeof(FakeController).Assembly); + })) + { + var client = filter.Host.GetTestClient(); + var profiler = await TimeMeasure.WithFuncAsync(client.GetAsync, "/fake/oneSecond"); + var serverTimings = profiler.Result.Headers.GetValues(ServerTiming.HeaderName).ToArray(); + + Assert.InRange(profiler.Elapsed, TimeSpan.Zero, TimeSpan.FromSeconds(5)); + Assert.True(profiler.Result.Headers.Contains("Server-Timing")); + Assert.Equal("redis", serverTimings[0].Split(';').First()); + Assert.Equal("restApi", serverTimings[1].Split(';').First()); + Assert.Equal("mvc", serverTimings[2].Split(';').First()); + + TestOutput.WriteLine(profiler.Elapsed.ToString()); + } + } + [Fact] public async Task OnActionExecutionAsync_ShouldTimeMeasureFakeController() { From a1957a59fca207dc25b83654ba70b2f27f897500 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 21:30:58 +0200 Subject: [PATCH 332/385] S2436 justification. --- src/Cuemon.Core/GlobalSuppressions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Cuemon.Core/GlobalSuppressions.cs b/src/Cuemon.Core/GlobalSuppressions.cs index 9d0d5e37c..60a79099a 100644 --- a/src/Cuemon.Core/GlobalSuppressions.cs +++ b/src/Cuemon.Core/GlobalSuppressions.cs @@ -257,3 +257,6 @@ [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Reflection.ActivatorFactory.CreateInstance``4(``0,``1,``2,System.Action{Cuemon.Reflection.ActivatorOptions})~``3")] [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Reflection.ActivatorFactory.CreateInstance``5(``0,``1,``2,``3,System.Action{Cuemon.Reflection.ActivatorOptions})~``4")] [assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Reflection.ActivatorFactory.CreateInstance``6(``0,``1,``2,``3,``4,System.Action{Cuemon.Reflection.ActivatorOptions})~``5")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Runtime.Dependency.Create``4(``0,``1,``2)~``3")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Runtime.Dependency.Create``5(``0,``1,``2,``3)~``4")] +[assembly: SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "By design; allow up till 5 arguments (more under certain conditions).", Scope = "member", Target = "~M:Cuemon.Runtime.Dependency.Create``6(``0,``1,``2,``3,``4)~``5")] From 7537dd6dc90453790d7be812850e19d40676eaef Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 21:51:49 +0200 Subject: [PATCH 333/385] New unit test. --- .../Assets/FakeController.cs | 7 +++ .../Diagnostics/FaultDescriptorFilterTest.cs | 44 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/FaultDescriptorFilterTest.cs diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs index 5ab31a3fe..1113bde3a 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Cuemon.AspNetCore.Mvc.Filters.Diagnostics; using Microsoft.AspNetCore.Mvc; @@ -36,5 +37,11 @@ public async Task GetAfter1SecondDecorated() await Task.Delay(TimeSpan.FromSeconds(1)); return Ok("Unit Test"); } + + [HttpGet("getResponse400")] + public IActionResult GetBadRequest() + { + throw new ValidationException("Unit Test"); + } } } \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/FaultDescriptorFilterTest.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/FaultDescriptorFilterTest.cs new file mode 100644 index 000000000..3c9dd0b96 --- /dev/null +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Diagnostics/FaultDescriptorFilterTest.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Cuemon.AspNetCore.Mvc.Assets; +using Cuemon.Extensions.Xunit; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics +{ + public class FaultDescriptorFilterTest : Test + { + public FaultDescriptorFilterTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task GetBadRequest_ShouldReturnBadRequest() + { + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.AddControllers(o => { o.Filters.Add(); }).AddApplicationPart(typeof(FakeController).Assembly).AddJsonOptions(o => + { + o.JsonSerializerOptions.MaxDepth = 0; + o.JsonSerializerOptions.IgnoreNullValues = true; + o.JsonSerializerOptions.IgnoreReadOnlyProperties = true; + }); + })) + { + var client = filter.Host.GetTestClient(); + var result = await client.GetAsync("/fake/getResponse400"); + + Assert.Equal(StatusCodes.Status400BadRequest, (int) result.StatusCode); + } + } + } +} \ No newline at end of file From 6774efd3d35903836858ea1d06271bc6922fa982 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 15 Oct 2020 22:49:53 +0200 Subject: [PATCH 334/385] Unit test of Cacheable filter. --- .../Assets/FakeController.cs | 13 +++ .../Cuemon.AspNetCore.Mvc.Tests.csproj | 1 + .../Cacheable/HttpCacheableFilterTest.cs | 81 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 test/Cuemon.AspNetCore.Mvc.Tests/Filters/Cacheable/HttpCacheableFilterTest.cs diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs index 1113bde3a..fa93c98ab 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/FakeController.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Cuemon.AspNetCore.Mvc.Filters.Diagnostics; +using Cuemon.Extensions.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc; namespace Cuemon.AspNetCore.Mvc.Assets @@ -43,5 +44,17 @@ public IActionResult GetBadRequest() { throw new ValidationException("Unit Test"); } + + [HttpGet("getCacheByEtag")] + public IActionResult GetEtag() + { + return Ok("Unit Test".MakeCacheable(s => Convertible.GetBytes(Generate.HashCode32(s)))); + } + + [HttpGet("getCacheByLastModified")] + public IActionResult GetLastModified() + { + return Ok("Unit Test".MakeCacheable(s => DateTime.UnixEpoch)); + } } } \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj b/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj index 7b3269ae1..d0696179f 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj @@ -5,6 +5,7 @@ + diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Cacheable/HttpCacheableFilterTest.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Cacheable/HttpCacheableFilterTest.cs new file mode 100644 index 000000000..ab5f6d737 --- /dev/null +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Cacheable/HttpCacheableFilterTest.cs @@ -0,0 +1,81 @@ +using System.Threading.Tasks; +using Cuemon.AspNetCore.Mvc.Assets; +using Cuemon.Extensions.Xunit; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Mvc.Filters.Cacheable +{ + public class HttpCacheableFilterTest : Test + { + public HttpCacheableFilterTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task GetEtag_ShouldReturnOkWithEtagAndSubsequentlyNotModified() + { + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.Configure(o => + { + o.Filters.Add(new HttpEntityTagHeaderFilter()); + }); + services.AddControllers(o => { o.Filters.Add(); }).AddApplicationPart(typeof(FakeController).Assembly); + })) + { + var client = filter.Host.GetTestClient(); + var result = await client.GetAsync("/fake/getCacheByEtag"); + var etag = result.Headers.ETag.ToString(); + + Assert.Equal(StatusCodes.Status200OK, (int) result.StatusCode); + TestOutput.WriteLine(etag); + + client.DefaultRequestHeaders.Add(HeaderNames.IfNoneMatch, etag); + + result = await client.GetAsync("/fake/getCacheByEtag"); + Assert.Equal(StatusCodes.Status304NotModified, (int) result.StatusCode); + } + } + + [Fact] + public async Task GetLastModified_ShouldReturnOkWithLastModifiedAndSubsequentlyNotModified() + { + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.Configure(o => + { + o.Filters.Add(new HttpLastModifiedHeaderFilter()); + }); + services.AddControllers(o => { o.Filters.Add(); }).AddApplicationPart(typeof(FakeController).Assembly); + })) + { + var client = filter.Host.GetTestClient(); + var result = await client.GetAsync("/fake/getCacheByLastModified"); + var lastModified = result.Content.Headers.LastModified.Value; + + Assert.Equal(StatusCodes.Status200OK, (int) result.StatusCode); + TestOutput.WriteLine(lastModified.ToString("O")); + + client.DefaultRequestHeaders.Add(HeaderNames.IfModifiedSince, lastModified.ToString("R")); + + result = await client.GetAsync("/fake/getCacheByLastModified"); + Assert.Equal(StatusCodes.Status304NotModified, (int) result.StatusCode); + } + } + } +} \ No newline at end of file From 614697ef3afb92e130dc012d8a5ff141a13e8c78 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 16 Oct 2020 01:34:13 +0200 Subject: [PATCH 335/385] Added release notes and updated DocFx namespace description. --- docfx/api/namespaces/Cuemon.Data.Integrity.md | 15 +++++++++++++- .../Properties/PackageReleaseNotes.txt | 20 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/Cuemon.Data.Integrity/Properties/PackageReleaseNotes.txt diff --git a/docfx/api/namespaces/Cuemon.Data.Integrity.md b/docfx/api/namespaces/Cuemon.Data.Integrity.md index 9ec436de3..ce6305ca2 100644 --- a/docfx/api/namespaces/Cuemon.Data.Integrity.md +++ b/docfx/api/namespaces/Cuemon.Data.Integrity.md @@ -2,4 +2,17 @@ uid: Cuemon.Data.Integrity summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Data.Integrity namespace contains types that provide ways for developers to determine and maintain integrity of data that is normally associated with an entity/resource. + +Availability: NET Standard 2.0 + +Related: [Cuemon.Extensions.Data.Integrity namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Data.Integrity.html) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Data.Integrity)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Data.Integrity)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Data.Integrity) + +NuGet packages 📦\ +[Cuemon.Data.Integrity (CI)](https://nuget.cuemon.net/packages/Cuemon.Data.Integrity)\ +[Cuemon.Data.Integrity (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Data.Integrity) \ No newline at end of file diff --git a/src/Cuemon.Data.Integrity/Properties/PackageReleaseNotes.txt b/src/Cuemon.Data.Integrity/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..e9721db55 --- /dev/null +++ b/src/Cuemon.Data.Integrity/Properties/PackageReleaseNotes.txt @@ -0,0 +1,20 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# Upgrade Steps +- The Cuemon.Integrity namespace was removed with this version +- Any former extension methods of the Cuemon.Integrity namespace was merged into the Cuemon.Extensions.Data.Integrity namespace +  +# Breaking Changes +- RENAMED ChecksumMethod enum in the Cuemon.Data.Integrity namespace to EntityDataIntegrityMethod (including rename of Default --> Unaltered) +- RENAMED ChecksumStrength enum in the Cuemon.Data.Integrity namespace to EntityDataIntegrityValidation (including rename of None --> Unspecified) +- RENAMED ICacheableIntegrity interface in the Cuemon.Data.Integrity namespace to IEntityDataIntegrity +- RENAMED ICacheableTimestamp interface in the Cuemon.Data.Integrity namespace to IEntityDataTimestamp +- RENAMED ICacheableEntity interface in the Cuemon.Data.Integrity namespace to IEntityInfo +  +# New Features +- ADDED CacheValidatorFactory class in the Cuemon.Data.Integrity namespace that provides access to factory methods for creating and configuring CacheValidator instances +- ADDED DataIntegrityFactory class in the Cuemon.Data.Integrity namespace that provides access to factory methods for creating and configuring implementations of the IDataIntegrity interface +- ADDED FileChecksumOptions class in the Cuemon.Data.Integrity namespace that specifies configuration options for FileInfo +- ADDED FileIntegrityOptions class in the Cuemon.Data.Integrity namespace that specifies configuration options for FileInfo +- ADDED IDataIntegrity interface in the Cuemon.Data.Integrity namespace determines the integrity of data \ No newline at end of file From 5f896b6ab954bd635d60438b630ab283c4615e53 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 16 Oct 2020 22:16:22 +0200 Subject: [PATCH 336/385] Updated package description, tags and release notes incl. DocFx namespace description. --- docfx/api/namespaces/Cuemon.Data.md | 15 ++++- src/Cuemon.Data/Cuemon.Data.csproj | 4 +- src/Cuemon.Data/DataReader.cs | 2 - .../Properties/PackageReleaseNotes.txt | 57 ++++++------------- 4 files changed, 32 insertions(+), 46 deletions(-) diff --git a/docfx/api/namespaces/Cuemon.Data.md b/docfx/api/namespaces/Cuemon.Data.md index 86b654a7e..fe7543ec0 100644 --- a/docfx/api/namespaces/Cuemon.Data.md +++ b/docfx/api/namespaces/Cuemon.Data.md @@ -2,4 +2,17 @@ uid: Cuemon.Data summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Data namespace contains types that provide ways to connect, build and manipulate different data sources. The namespace is an addition to the System.Data namespace. + +Availability: NET Standard 2.0 + +Complements: [System.Data namespace](https://docs.microsoft.com/en-us/dotnet/api/system.Data?view=netstandard-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Data)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Data)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Data) + +NuGet packages 📦\ +[Cuemon.Data (CI)](https://nuget.cuemon.net/packages/Cuemon.Data)\ +[Cuemon.Data (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Data) \ No newline at end of file diff --git a/src/Cuemon.Data/Cuemon.Data.csproj b/src/Cuemon.Data/Cuemon.Data.csproj index 744ca737b..9ef24c4b2 100644 --- a/src/Cuemon.Data/Cuemon.Data.csproj +++ b/src/Cuemon.Data/Cuemon.Data.csproj @@ -8,8 +8,8 @@ Cuemon.Data Cuemon.Data - The Cuemon.Data namespace contains abstractions related to the System.Data namespace. - database db abstractions dto data-transfer row column bulk-copy + The Cuemon.Data namespace contains types that provide ways to connect, build and manipulate different data sources. The namespace is an addition to the System.Data namespace. + database db abstractions dto data-transfer row column bulk-copy dsv csv xml-data-reader dsv-data-reader concurrent-dsv-data-reader in-operator token-builder diff --git a/src/Cuemon.Data/DataReader.cs b/src/Cuemon.Data/DataReader.cs index cb7c40293..48eb398cc 100644 --- a/src/Cuemon.Data/DataReader.cs +++ b/src/Cuemon.Data/DataReader.cs @@ -8,8 +8,6 @@ namespace Cuemon.Data { /// /// Provides a generic way of reading a forward-only stream of rows from a based data source. This is an abstract class. - /// Implements the - /// Implements the /// /// The type of the value that this will read. /// diff --git a/src/Cuemon.Data/Properties/PackageReleaseNotes.txt b/src/Cuemon.Data/Properties/PackageReleaseNotes.txt index 83c78cee7..d10352983 100644 --- a/src/Cuemon.Data/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Data/Properties/PackageReleaseNotes.txt @@ -2,49 +2,24 @@ Availability: NET Standard 2.0   # Upgrade Steps +- The Cuemon.Data.XmlClient assembly and namespace was removed with this version +- Types found in the Cuemon.Data.XmlClient namespace was merged into the Cuemon.Data.Xml namespace - To use the earlier built-in support for Microsoft SQL Server, please refer to the Cuemon.Data.SqlClient namespace, as it has been merged and refactored out of this assembly +- Any former extension methods of the Cuemon.Data namespace was merged into the Cuemon.Extensions.Data namespace   # Breaking Changes -- REMOVED StringFormatter class from the Cuemon namespace -- REMOVED StandardizedDateTimeFormatPattern enum from the Cuemon namespace -- MOVED AsyncOptions class in the Cuemon.Threading namespace to its own assembly (by the same name and namespace) -- REMOVED JsonWebToken class from the Cuemon.Security.Web namespace -- REMOVED JsonWebTokenHashAlgorithm class from the Cuemon.Security.Web namespace -- REMOVED JsonWebTokenHashAlgorithmConverter class from the Cuemon.Security.Web namespace -- REMOVED JsonWebTokenHeader class from the Cuemon.Security.Web namespace -- REMOVED JsonWebTokenPayload class from the Cuemon.Security.Web namespace -- REMOVED Obfuscator class from the Cuemon.Security namespace -- REMOVED ObfuscatorMapping class from the Cuemon.Security namespace -- REMOVED SecurityToken class from the Cuemon.Security namespace -- REMOVED SecurityTokenSettings class from the Cuemon.Security namespace (replaced with SignedUriOptions in the Cuemon.Extensions.Net.Security namespace) -- REMOVED SecurityUtility class from the Cuemon.Security namespace -- REMOVED AssemblyExtensions class from the Cuemon.Reflection namespace -- MOVED MemberInfoExtensions class from the Cuemon.Reflection namespace to Cuemon.Extensions.Reflection namespace -- REMOVED MethodBaseConverterExtensions class from the Cuemon.Reflection namespace -- MOVED LatencyException class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) -- MOVED TransientOperation class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) -- MOVED TransientFaultEvidence class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) -- MOVED TransientFaultException class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) -- MOVED TransientOperation class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) -- MOVED TransientOperationOptions class in the Cuemon namespace to its own assembly and namespace (Cuemon.Resilience) -- REMOVED IMessageLocalizer interface from the Cuemon.Globalization namespace +- REFACTORED XmlDataReader class in the Cuemon.Data.Xml namespace to match and inherit from DataReader +- REFACTORED DataManager class in the Cuemon.Data namespace to have a higher cohesion and lower coupling +- RENAMED StringDataReader class in the Cuemon.Data namespace to DataReader{T} (including major refactoring of the underlying code) +- RENAMED DataParameterEqualityComparer class in the Cuemon.Data namespace to DbParameterEqualityComparer +- REMOVED DataTransferSorter class from the Cuemon.Data namespace +- REMOVED CsvDataReader class from the Cuemon.Data.CsvClient namespace (including the namespace) +- MERGED Cuemon.Data.SqlClient namespace to its own assembly +- REFACTORED InOperator class in the Cuemon.Data namespace to be less complex and make usage of InOperatorResult +- REMOVED QueryUtility class from the Cuemon.Data namespace   # New Features -- ADDED ResourceAttribute class in the Cuemon.Globalization namespace that provides a generic way to support localization on attribute decorated methods -- -  -# Bug Fixes -- -- -  -# Improvements -- -- -  -# Quality Actions -- -- -  -# Other Changes -- -- \ No newline at end of file +- ADDED ConcurrentDsvDataReader class in the Cuemon.Data namespace that provides a concurrent way of reading a forward-only stream of rows from a DSV (Delimiter Separated Values) based data source +- ADDED DsvDataReader class in the Cuemon.Data namespace that provides a way of reading a forward-only stream of rows from a DSV (Delimiter Separated Values) based data source +- ADDED InOperatorResult class in the Cuemon.Data namespace that provides the result of an InOperator{T} operation +- ADDED TokenBuilder class in the Cuemon.Data namespace that represents a mutable string of characters optimized for tokens \ No newline at end of file From 7bcb853cae7290f1af966c541de26cbe04fc7e1f Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 16 Oct 2020 22:16:32 +0200 Subject: [PATCH 337/385] Rephrasing. --- src/Cuemon.Xml/Properties/PackageReleaseNotes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Xml/Properties/PackageReleaseNotes.txt b/src/Cuemon.Xml/Properties/PackageReleaseNotes.txt index 6da38bf73..6678fc4bf 100644 --- a/src/Cuemon.Xml/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Xml/Properties/PackageReleaseNotes.txt @@ -2,7 +2,7 @@ Availability: NET Standard 2.0   # Upgrade Steps -- The Cuemon.Serialization.Xml namespace was removed with this version +- The Cuemon.Serialization.Xml assembly and namespace was removed with this version - Any XML serialization found in the Cuemon.Serialization.Xml namespace was merged into the Cuemon.Xml.Serialization namespace - Any former extension methods of the Cuemon.Xml namespace was merged into the Cuemon.Extensions.Xml namespace - The extent of refactoring applied to this project resulted in so many breaking changes that a git diff is advisable From bd72da816f1c476cf63f7e3d7c6bdcf4aaecd114 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Sat, 17 Oct 2020 19:28:42 +0200 Subject: [PATCH 338/385] Refactored to comply with industry standards. --- .../FaultResolverDecoratorExtensions.cs | 3 +- .../Diagnostics/FaultDescriptorOptions.cs | 1 - .../Throttling/ThrottlingSentinelAttribute.cs | 17 +++++-- .../TooManyRequestsObjectResult.cs | 20 +++++++++ .../TooManyRequestsResult.cs | 18 ++++++++ .../Cuemon.AspNetCore.csproj | 4 +- .../Http/HttpContextDecoratorExtensions.cs | 12 ++++- .../RetryConditionScope.cs} | 6 +-- .../Throttling/ThrottlingSentinelOptions.cs | 36 ++++++++++----- .../Diagnostics/FaultResolverExtensions.cs | 1 - .../Cuemon.Extensions.Hosting.csproj | 2 +- .../Assets/ExceptionFilter.cs | 22 +++++++++ .../Cuemon.AspNetCore.Mvc.Tests.csproj | 1 + .../ThrottlingSentinelAttributeTest.cs | 45 +++++++++++++++++++ .../ThrottlingSentinelMiddlewareTest.cs | 1 - 15 files changed, 161 insertions(+), 28 deletions(-) create mode 100644 src/Cuemon.AspNetCore.Mvc/TooManyRequestsObjectResult.cs create mode 100644 src/Cuemon.AspNetCore.Mvc/TooManyRequestsResult.cs rename src/Cuemon.AspNetCore/Http/{Throttling/ThrottlingRetryAfterHeader.cs => Headers/RetryConditionScope.cs} (57%) create mode 100644 test/Cuemon.AspNetCore.Mvc.Tests/Assets/ExceptionFilter.cs diff --git a/src/Cuemon.AspNetCore.Mvc/Extensions/Filters/Diagnostics/FaultResolverDecoratorExtensions.cs b/src/Cuemon.AspNetCore.Mvc/Extensions/Filters/Diagnostics/FaultResolverDecoratorExtensions.cs index d683840f1..71fd7773a 100644 --- a/src/Cuemon.AspNetCore.Mvc/Extensions/Filters/Diagnostics/FaultResolverDecoratorExtensions.cs +++ b/src/Cuemon.AspNetCore.Mvc/Extensions/Filters/Diagnostics/FaultResolverDecoratorExtensions.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using Cuemon.AspNetCore.Http; -using Cuemon.AspNetCore.Mvc.Filters.Diagnostics; -namespace Cuemon.AspNetCore.Mvc.Extensions.Filters.Diagnostics +namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics { /// /// Extension methods for the class tailored to adhere the decorator pattern. diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorOptions.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorOptions.cs index da3243bd4..a2c02d43e 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorOptions.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorOptions.cs @@ -6,7 +6,6 @@ using Cuemon.AspNetCore.Http; using Cuemon.AspNetCore.Http.Headers; using Cuemon.AspNetCore.Http.Throttling; -using Cuemon.AspNetCore.Mvc.Extensions.Filters.Diagnostics; using Microsoft.AspNetCore.Http; namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelAttribute.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelAttribute.cs index 8a1d73737..6b7c9767b 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelAttribute.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Throttling/ThrottlingSentinelAttribute.cs @@ -1,4 +1,5 @@ using System; +using Cuemon.AspNetCore.Http.Headers; using Cuemon.AspNetCore.Http.Throttling; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters; @@ -26,11 +27,12 @@ protected ThrottlingSentinelAttribute(int rateLimit, double window, TimeUnit win Window = window; WindowUnit = windowUnit; UseRetryAfterHeader = options.UseRetryAfterHeader; - RetryAfterHeader = options.RetryAfterHeader; + RetryAfterScope = options.RetryAfterScope; TooManyRequestsMessage = options.TooManyRequestsMessage; RateLimitHeaderName = options.RateLimitHeaderName; RateLimitRemainingHeaderName = options.RateLimitRemainingHeaderName; RateLimitResetHeaderName = options.RateLimitResetHeaderName; + RateLimitResetScope = options.RateLimitResetScope; } private int RateLimit { get; } @@ -55,7 +57,7 @@ protected ThrottlingSentinelAttribute(int rateLimit, double window, TimeUnit win /// Gets or sets the preferred Retry-After HTTP header value that conforms with RFC 2616. /// /// The preferred Retry-After HTTP header value that conforms with RFC 2616. - public ThrottlingRetryAfterHeader RetryAfterHeader { get; set; } + public RetryConditionScope RetryAfterScope { get; set; } /// /// Gets or sets the name of the rate limit remaining HTTP header. @@ -75,6 +77,12 @@ protected ThrottlingSentinelAttribute(int rateLimit, double window, TimeUnit win /// The name of the rate limit reset HTTP header. public string RateLimitResetHeaderName { get; set; } + /// + /// Gets or sets the preferred rate limit reset HTTP header value that conforms with RFC 7231. + /// + /// The preferred rate limit reset HTTP header value that conforms with RFC 7231. + public RetryConditionScope RateLimitResetScope { get; set; } + /// /// Creates an instance of the executable filter. /// @@ -88,11 +96,12 @@ public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) Quota = new ThrottleQuota(RateLimit, Window, WindowUnit), ContextResolver = UniqueContextResolver, UseRetryAfterHeader = UseRetryAfterHeader, - RetryAfterHeader = RetryAfterHeader, + RetryAfterScope = RetryAfterScope, TooManyRequestsMessage = TooManyRequestsMessage, RateLimitHeaderName = RateLimitHeaderName, RateLimitRemainingHeaderName = RateLimitRemainingHeaderName, - RateLimitResetHeaderName = RateLimitResetHeaderName + RateLimitResetHeaderName = RateLimitResetHeaderName, + RateLimitResetScope = RateLimitResetScope }), tc); } diff --git a/src/Cuemon.AspNetCore.Mvc/TooManyRequestsObjectResult.cs b/src/Cuemon.AspNetCore.Mvc/TooManyRequestsObjectResult.cs new file mode 100644 index 000000000..a64e2371f --- /dev/null +++ b/src/Cuemon.AspNetCore.Mvc/TooManyRequestsObjectResult.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Cuemon.AspNetCore.Mvc +{ + /// + /// An that when executed will produce a Too Many Requests (429) response. + /// + public class TooManyRequestsObjectResult : ObjectResult + { + /// + /// Initializes a new instance of the class. + /// + /// Contains the errors to be returned to the client. + public TooManyRequestsObjectResult(object error) : base(error) + { + StatusCode = StatusCodes.Status429TooManyRequests; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Mvc/TooManyRequestsResult.cs b/src/Cuemon.AspNetCore.Mvc/TooManyRequestsResult.cs new file mode 100644 index 000000000..c72f75dc5 --- /dev/null +++ b/src/Cuemon.AspNetCore.Mvc/TooManyRequestsResult.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Cuemon.AspNetCore.Mvc +{ + /// + /// An that returns a TooManyRequests (429) response. + /// + public class TooManyRequestsResult : StatusCodeResult + { + /// + /// Initializes a new instance of the class. + /// + public TooManyRequestsResult() : base(StatusCodes.Status429TooManyRequests) + { + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj index 68ad01a70..b2ff2fff7 100644 --- a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj +++ b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj @@ -8,8 +8,8 @@ Cuemon.AspNetCore Cuemon.AspNetCore - The Cuemon.AspNetCore namespace contains abundant features related to the Microsoft.AspNetCore namespace. - configurable-middleware middleware user-agent-sentinel throttling hosting-environment cache-busting application-builder-factory + The Cuemon.AspNetCore namespace contains types focusing on providing means for easier plumber coding in the ASP.NET Core pipeline while serving some concrete implementation of the shell as well. The namespace is an addition to the Microsoft.AspNetCore namespace. + configurable-middleware middleware http-exception-descriptor throttling-sentinel-middleware user-agent-sentinel-middleware request-identifier-middleware correlation-identifier-middleware hosting-environment-middleware server-timing cache-busting middleware-builder-factory diff --git a/src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs b/src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs index fe53d6e7d..442e0b7be 100644 --- a/src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs +++ b/src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Linq; using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Cuemon.AspNetCore.Http.Headers; @@ -56,7 +57,16 @@ public static async Task InvokeThrottlerSentinelAsync(this IDecorator tr.Quota.RateLimit && tr.Expires > utcNow) { var message = options.ResponseBroker?.Invoke(delta, reset); diff --git a/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingRetryAfterHeader.cs b/src/Cuemon.AspNetCore/Http/Headers/RetryConditionScope.cs similarity index 57% rename from src/Cuemon.AspNetCore/Http/Throttling/ThrottlingRetryAfterHeader.cs rename to src/Cuemon.AspNetCore/Http/Headers/RetryConditionScope.cs index e2894c8dc..e8358e31f 100644 --- a/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingRetryAfterHeader.cs +++ b/src/Cuemon.AspNetCore/Http/Headers/RetryConditionScope.cs @@ -1,9 +1,9 @@ -namespace Cuemon.AspNetCore.Http.Throttling +namespace Cuemon.AspNetCore.Http.Headers { /// - /// Specifies a set of values defining what value to use with a HTTP Retry-After header. + /// Specifies a set of values defining what value to use with a given HTTP header in regards to a retry condition. Recommended value is always as it does not rely on clock synchronization and is resilient to clock skew between client and server. /// - public enum ThrottlingRetryAfterHeader + public enum RetryConditionScope { /// /// A non-negative decimal integer indicating the seconds to delay after the response is received. diff --git a/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelOptions.cs b/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelOptions.cs index f132c457b..f3a17eec8 100644 --- a/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelOptions.cs +++ b/src/Cuemon.AspNetCore/Http/Throttling/ThrottlingSentinelOptions.cs @@ -2,6 +2,7 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using Cuemon.AspNetCore.Http.Headers; using Microsoft.AspNetCore.Http; using Microsoft.Net.Http.Headers; @@ -35,12 +36,16 @@ public class ThrottlingSentinelOptions /// X-RateLimit-Reset /// /// + /// + /// + /// + /// /// /// true /// /// - /// - /// ThrottlingRetryAfterHeader.DeltaSeconds + /// + /// /// /// /// @@ -62,23 +67,24 @@ public class ThrottlingSentinelOptions /// public ThrottlingSentinelOptions() { - RateLimitHeaderName = "X-RateLimit-Limit"; - RateLimitRemainingHeaderName = "X-RateLimit-Remaining"; - RateLimitResetHeaderName = "X-RateLimit-Reset"; + RateLimitHeaderName = "RateLimit-Limit"; + RateLimitRemainingHeaderName = "RateLimit-Remaining"; + RateLimitResetHeaderName = "RateLimit-Reset"; + RateLimitResetScope = RetryConditionScope.DeltaSeconds; UseRetryAfterHeader = true; - RetryAfterHeader = ThrottlingRetryAfterHeader.DeltaSeconds; + RetryAfterScope = RetryConditionScope.DeltaSeconds; TooManyRequestsMessage = "Throttling rate limit quota violation. Quota limit exceeded."; ResponseBroker = (delta, reset) => { var message = new HttpResponseMessage((HttpStatusCode) StatusCodes.Status429TooManyRequests); if (UseRetryAfterHeader) { - switch (RetryAfterHeader) + switch (RetryAfterScope) { - case ThrottlingRetryAfterHeader.DeltaSeconds: + case RetryConditionScope.DeltaSeconds: message.Headers.Add(HeaderNames.RetryAfter, new RetryConditionHeaderValue(delta).ToString()); break; - case ThrottlingRetryAfterHeader.HttpDate: + case RetryConditionScope.HttpDate: message.Headers.Add(HeaderNames.RetryAfter, new RetryConditionHeaderValue(reset).ToString()); break; } @@ -130,6 +136,12 @@ public ThrottlingSentinelOptions() /// The name of the rate limit reset HTTP header. public string RateLimitResetHeaderName { get; set; } + /// + /// Gets or sets the preferred rate limit reset HTTP header value that conforms with RFC 7231. + /// + /// The preferred rate limit reset HTTP header value that conforms with RFC 7231. + public RetryConditionScope RateLimitResetScope { get; set; } + /// /// Gets or sets a value indicating whether to include a Retry-After HTTP header specifying how long to wait before making a new request. /// @@ -137,9 +149,9 @@ public ThrottlingSentinelOptions() public bool UseRetryAfterHeader { get; set; } /// - /// Gets or sets the preferred Retry-After HTTP header value that conforms with RFC 2616. + /// Gets or sets the preferred Retry-After HTTP header value that conforms with RFC 7231. /// - /// The preferred Retry-After HTTP header value that conforms with RFC 2616. - public ThrottlingRetryAfterHeader RetryAfterHeader { get; set; } + /// The preferred Retry-After HTTP header value that conforms with RFC 7231. + public RetryConditionScope RetryAfterScope { get; set; } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Diagnostics/FaultResolverExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Diagnostics/FaultResolverExtensions.cs index ab44cfee7..fa228f67a 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Diagnostics/FaultResolverExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Diagnostics/FaultResolverExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Cuemon.AspNetCore.Http; -using Cuemon.AspNetCore.Mvc.Extensions.Filters.Diagnostics; using Cuemon.AspNetCore.Mvc.Filters.Diagnostics; namespace Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics diff --git a/src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj b/src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj index 9071976b5..d1359f28f 100644 --- a/src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj +++ b/src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj @@ -17,7 +17,7 @@ - + diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Assets/ExceptionFilter.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/ExceptionFilter.cs new file mode 100644 index 000000000..a8d81931c --- /dev/null +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Assets/ExceptionFilter.cs @@ -0,0 +1,22 @@ +using Cuemon.AspNetCore.Http.Throttling; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Cuemon.AspNetCore.Mvc.Assets +{ + public class ExceptionFilter : ExceptionFilterAttribute + { + public override void OnException(ExceptionContext context) + { + if (context.ActionDescriptor is ControllerActionDescriptor) + { + var exception = context.Exception; + if (exception is ThrottlingException) + { + context.ExceptionHandled = true; + context.Result = new TooManyRequestsObjectResult(exception.Message); + } + } + } + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj b/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj index d0696179f..6fa916af4 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Cuemon.AspNetCore.Mvc.Tests.csproj @@ -7,6 +7,7 @@ + diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Throttling/ThrottlingSentinelAttributeTest.cs b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Throttling/ThrottlingSentinelAttributeTest.cs index eca551128..a812573aa 100644 --- a/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Throttling/ThrottlingSentinelAttributeTest.cs +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Filters/Throttling/ThrottlingSentinelAttributeTest.cs @@ -1,10 +1,14 @@ using System; +using System.Linq; +using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using Cuemon.AspNetCore.Http.Throttling; using Cuemon.AspNetCore.Mvc.Assets; +using Cuemon.Extensions; using Cuemon.Extensions.AspNetCore.Http.Throttling; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; @@ -86,6 +90,47 @@ public async Task Bearer_ShouldThrottleAndThenRehydrateAfterWindowHasPassed() Assert.Equal(5, ce.Total); } + [Fact] + public async Task Bearer_VerifyHeadersAreSetCorrectly() + { + using (var filter = MvcFilterTestFactory.CreateMvcFilterTest(app => + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, services => + { + services.AddControllers(o => { o.Filters.Add(); }).AddApplicationPart(typeof(FakeController).Assembly); + services.AddMemoryThrottlingCache(); + })) + { + var client = filter.Host.GetTestClient(); + + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", nameof(Bearer_VerifyHeadersAreSetCorrectly)); + + HttpResponseMessage result = null; + for (var i = 0; i < 10; i++) + { + result = await client.GetAsync("/fake"); + Assert.Equal(StatusCodes.Status200OK, (int)result.StatusCode); + Assert.Equal("Unit Test", await result.Content.ReadAsStringAsync()); + } + + result = await client.GetAsync("/fake"); + + var retryAfter = result.Headers.RetryAfter.Delta.Value.TotalSeconds; + var ratelimitReset = result.Headers.GetValues("RateLimit-Reset").Single().As(); + + Assert.Equal(StatusCodes.Status429TooManyRequests, (int)result.StatusCode); + Assert.Equal("Throttling rate limit quota violation. Quota limit exceeded.", await result.Content.ReadAsStringAsync()); + Assert.Contains("Retry-After", result.Headers.Select(pair => pair.Key)); + Assert.Contains("RateLimit-Limit", result.Headers.Select(pair => pair.Key)); + Assert.Contains("RateLimit-Remaining", result.Headers.Select(pair => pair.Key)); + Assert.Contains("RateLimit-Reset", result.Headers.Select(pair => pair.Key)); + + Assert.Equal(retryAfter, ratelimitReset); + } + } + public override void ConfigureServices(IServiceCollection services) { services.AddControllers().AddApplicationPart(typeof(FakeController).Assembly); diff --git a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs index a557407bf..1340fcbc1 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs @@ -84,7 +84,6 @@ public async Task InvokeAsync_ShouldRehydrate() { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; var options = middleware.ServiceProvider.GetRequiredService>(); - var cache = middleware.ServiceProvider.GetRequiredService(); var pipeline = middleware.Application.Build(); for (var i = 0; i < 10; i++) From 0a9760b0616271b177ad347ddf1c99ea55a9f87c Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Sat, 17 Oct 2020 19:32:32 +0200 Subject: [PATCH 339/385] Added, DocFx namespace description and applied consequence changes after rename of UseMiddlewareConfigurable --> UseConfigurableMiddleware. --- .../namespaces/Cuemon.AspNetCore.Builder.md | 15 ++++++++++++- .../Cuemon.AspNetCore.Configuration.md | 15 ++++++++++++- .../Cuemon.AspNetCore.Diagnostics.md | 22 +++++++++++++++++++ .../namespaces/Cuemon.AspNetCore.Hosting.md | 15 ++++++++++++- .../Cuemon.AspNetCore.Http.Headers.md | 15 ++++++++++++- .../Cuemon.AspNetCore.Http.Throttling.md | 15 ++++++++++++- .../api/namespaces/Cuemon.AspNetCore.Http.md | 17 +++++++++++++- docfx/api/namespaces/Cuemon.AspNetCore.md | 15 ++++++++++++- .../namespaces/Cuemon.Extensions.Hosting.md | 20 ++++++++++++++++- .../BasicAuthenticationMiddleware.cs | 2 +- .../DigestAccessAuthenticationMiddleware.cs | 2 +- .../HmacAuthenticationMiddleware.cs | 2 +- .../Builder/MiddlewareBuilderFactory.cs | 2 +- .../Builder/ApplicationBuilderExtensions.cs | 10 ++++----- 14 files changed, 150 insertions(+), 17 deletions(-) create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Diagnostics.md diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Builder.md b/docfx/api/namespaces/Cuemon.AspNetCore.Builder.md index ed6dadbf2..3f70a4936 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Builder.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Builder.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore.Builder summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Builder namespace contains types that supports adding either middleware or configurable middleware types to the application request pipeline. The namespace is an addition to the Microsoft.AspNetCore.Builder namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Related: [Cuemon.Extensions.AspNetCore.Builder namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Builder.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md b/docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md index 05bc718a6..4254f53ae 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore.Configuration summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Configuration namespace contains types that provides a way to support a [cache busting strategy](https://www.keycdn.com/support/what-is-cache-busting). + +Availability: NET Standard 2.0, NET Core 3.0 + +Related: [Cuemon.Extensions.AspNetCore.Mvc.Configuration namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Mvc.Configuration.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Diagnostics.md b/docfx/api/namespaces/Cuemon.AspNetCore.Diagnostics.md new file mode 100644 index 000000000..3021d324a --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Diagnostics.md @@ -0,0 +1,22 @@ +--- +uid: Cuemon.AspNetCore.Diagnostics +summary: *content +--- +The Cuemon.AspNetCore.Diagnostics namespace contains types that provides a way to support the Server-Timing header for communicating metrics about the request-response cycle to an user agent. The namespace is an addition to the Microsoft.AspNetCore.Diagnostics namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Diagnostics namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.diagnostics?view=aspnetcore-2.0) 🔗 + +Related: [Cuemon.AspNetCore.Mvc.Filters.Diagnostics namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.html) 📘 + +Related: [Cuemon.Extensions.AspNetCore.Diagnostics namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Diagnostics.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md b/docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md index ce0004378..3a848655b 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore.Hosting summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Hosting namespace contains types that provides middleware for determining the hosting environment. The namespace is an addition to the Microsoft.AspNetCore.Hosting namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Hosting namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.hosting?view=aspnetcore-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md index 6248be9d3..f28eb3ba8 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore.Http.Headers summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Http.Headers namespace contains types that provides a set of middleware components tied to HTTP headers. The namespace is an addition to the Microsoft.AspNetCore.Http.Headers namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Http.Headers namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.headers?view=aspnetcore-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md index 33207b750..f66e9f4d0 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore.Http.Throttling summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Http.Throttling namespace contains types that provides a middleware based throttling mechanism by specifying allowed quota and window duration of HTTP requests tied to a custom context (eg. IP-address, Authorization header, etc.). + +Availability: NET Standard 2.0, NET Core 3.0 + +Related: [Cuemon.Extensions.AspNetCore.Http.Throttling namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Http.Throttling.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Http.md b/docfx/api/namespaces/Cuemon.AspNetCore.Http.md index fc382f936..57f397912 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Http.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Http.md @@ -2,4 +2,19 @@ uid: Cuemon.AspNetCore.Http summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Http namespace contains types focusing on ways to provide developer friendly exception messages optimized for open- and otherwise public application programming interfaces (API). The namespace is an addition to the Microsoft.AspNetCore.Http namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Http namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http?view=aspnetcore-2.0) 🔗 + +Related: [Cuemon.Extensions.AspNetCore.Http namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Http.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.md b/docfx/api/namespaces/Cuemon.AspNetCore.md index 23ca69cbc..abfdb80f6 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore namespace contains types focusing on providing means for easier plumber coding in the ASP.NET Core pipeline while serving some concrete implementation of the shell as well. The namespace is an addition to the Microsoft.AspNetCore namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore?view=aspnetcore-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Extensions.Hosting.md b/docfx/api/namespaces/Cuemon.Extensions.Hosting.md index 5328ee946..eddef1bf3 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Hosting.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Hosting.md @@ -4,4 +4,22 @@ summary: *content --- The Cuemon.Extensions.Hosting namespace contains extension methods and features related to the Microsoft.Extensions.Hosting namespace. -Availability: NET Standard 2.0, NET Core 3.0 \ No newline at end of file +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.Extensions.Hosting namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting?view=dotnet-plat-ext-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Hosting)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Extensions.Hosting)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Extensions.Hosting) + +NuGet packages 📦\ +[Cuemon.Extensions.Hosting (CI)](https://nuget.cuemon.net/packages/Cuemon.Extensions.Hosting)\ +[Cuemon.Extensions.Hosting (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Extensions.Hosting) + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|IHostEnvironment|⬇️|`IsLocalDevelopment`, `IsNonProduction`| +|IHostingEnvironment|⬇️|`IsLocalDevelopment`, `IsNonProduction`| \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs index bc829930c..3105a7450 100644 --- a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs @@ -99,7 +99,7 @@ public static class BasicAuthenticationBuilderExtension /// A reference to this instance after the operation has completed. public static IApplicationBuilder UseBasicAuthentication(this IApplicationBuilder builder, Action setup = null) { - return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs index 2f5e86897..a1ccb659c 100644 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs @@ -177,7 +177,7 @@ public static class DigestAccessAuthenticationBuilderExtension /// A reference to this instance after the operation has completed. public static IApplicationBuilder UseDigestAccessAuthentication(this IApplicationBuilder builder, Action setup = null) { - return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs index d81d25d19..109087602 100644 --- a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs @@ -103,7 +103,7 @@ public static class HmacAuthenticationBuilderExtension /// A reference to this instance after the operation has completed. public static IApplicationBuilder UseHmacAuthentication(this IApplicationBuilder builder, Action setup = null) { - return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Builder/MiddlewareBuilderFactory.cs b/src/Cuemon.AspNetCore/Builder/MiddlewareBuilderFactory.cs index d34a94132..4d136e840 100644 --- a/src/Cuemon.AspNetCore/Builder/MiddlewareBuilderFactory.cs +++ b/src/Cuemon.AspNetCore/Builder/MiddlewareBuilderFactory.cs @@ -28,7 +28,7 @@ public static IApplicationBuilder UseMiddleware(IApplicationBuilder /// The instance. /// The which need to be configured. /// The instance. - public static IApplicationBuilder UseMiddlewareConfigurable(IApplicationBuilder builder, Action setup = null) + public static IApplicationBuilder UseConfigurableMiddleware(IApplicationBuilder builder, Action setup = null) where TMiddleware : ConfigurableMiddlewareCore where TOptions : class, new() { diff --git a/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs index 38e001f1b..9056fa9ab 100644 --- a/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs @@ -21,7 +21,7 @@ public static class ApplicationBuilderExtensions /// Default HTTP header name is X-Hosting-Environment. public static IApplicationBuilder UseHostingEnvironment(this IApplicationBuilder builder, Action setup = null) { - return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); } /// @@ -33,7 +33,7 @@ public static IApplicationBuilder UseHostingEnvironment(this IApplicationBuilder /// Default HTTP header name is X-Correlation-ID. public static IApplicationBuilder UseCorrelationIdentifier(this IApplicationBuilder builder, Action setup = null) { - return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); } /// @@ -45,7 +45,7 @@ public static IApplicationBuilder UseCorrelationIdentifier(this IApplicationBuil /// Default HTTP header name is X-Request-ID. public static IApplicationBuilder UseRequestIdentifier(this IApplicationBuilder builder, Action setup = null) { - return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); } /// @@ -56,7 +56,7 @@ public static IApplicationBuilder UseRequestIdentifier(this IApplicationBuilder /// A reference to this instance after the operation has completed. public static IApplicationBuilder UseUserAgentSentinel(this IApplicationBuilder builder, Action setup = null) { - return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); } /// @@ -67,7 +67,7 @@ public static IApplicationBuilder UseUserAgentSentinel(this IApplicationBuilder /// A reference to this instance after the operation has completed. public static IApplicationBuilder UseThrottlingSentinel(this IApplicationBuilder builder, Action setup = null) { - return MiddlewareBuilderFactory.UseMiddlewareConfigurable(builder, setup); + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); } } } \ No newline at end of file From bfd9d31ff36e35f3d4a42d80c393f78ea2b2747c Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Sun, 18 Oct 2020 01:26:49 +0200 Subject: [PATCH 340/385] Updated DocFx namespace descriptions. --- .../Cuemon.AspNetCore.Mvc.Filters.Cacheable.md | 17 ++++++++++++++++- ...Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md | 17 ++++++++++++++++- .../Cuemon.AspNetCore.Mvc.Filters.Headers.md | 15 ++++++++++++++- ...uemon.AspNetCore.Mvc.Filters.ModelBinding.md | 15 ++++++++++++++- .../Cuemon.AspNetCore.Mvc.Filters.Throttling.md | 15 ++++++++++++++- .../namespaces/Cuemon.AspNetCore.Mvc.Filters.md | 15 ++++++++++++++- docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md | 17 ++++++++++++++++- 7 files changed, 104 insertions(+), 7 deletions(-) diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md index 3b375e569..72b4176d8 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md @@ -2,4 +2,19 @@ uid: Cuemon.AspNetCore.Mvc.Filters.Cacheable summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace contains types that specializes in cache expiration and validation models. The namespace is an addition to the Microsoft.AspNetCore.Mvc.Filters namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Mvc.Filters namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters?view=aspnetcore-2.0) 🔗 + +Related: [Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Mvc)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Mvc) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md index 1ad5c5ffd..4ed78307e 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md @@ -2,4 +2,19 @@ uid: Cuemon.AspNetCore.Mvc.Filters.Diagnostics summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Mvc.Diagnostics namespace contains types that provide ways for developers to describe exceptions including evidence to why an operation faulted optimized for open- and otherwise public application programming interfaces (API). The namespace is an addition to the Microsoft.AspNetCore.Mvc.Filters namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Mvc.Filters namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters?view=aspnetcore-2.0) 🔗 + +Related: [Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Mvc)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Mvc) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md index 37a99e300..3f29ae7e6 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore.Mvc.Filters.Headers summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Mvc.Headers namespace contains types that provide filters explicitly written to different types of HTTP headers. The namespace is an addition to the Microsoft.AspNetCore.Mvc.Filters namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Mvc.Filters namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters?view=aspnetcore-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Mvc)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Mvc) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md index bed19f8c7..dc93f718d 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore.Mvc.Filters.ModelBinding summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Mvc.ModelBinding namespace contains types that alters the built-in way of doing model binding. The namespace is an addition to the Microsoft.AspNetCore.Mvc.ModelBinding namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Mvc.ModelBinding namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.modelbinding?view=aspnetcore-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Mvc)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Mvc) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md index 29d207362..4a18bd8c0 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore.Mvc.Filters.Throttling summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Mvc.Filters.Throttling namespace contains types that provides filter based throttling mechanism by specifying allowed quota and window duration of HTTP requests tied to a custom context (eg. IP-address, Authorization header, etc.). The namespace is an addition to the Microsoft.AspNetCore.Mvc.Filters namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Mvc.Filters namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters?view=aspnetcore-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Mvc)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Mvc) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md index 282f3a9c3..81945d633 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore.Mvc.Filters summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Mvc.Filters namespace contains types that supports a generic way of working with built-in interfaces providing ready-to-use class abstractions. The namespace is an addition to the Microsoft.AspNetCore.Mvc.Filters namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Mvc.Filters namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters?view=aspnetcore-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Mvc)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Mvc) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md index a205a70a2..92921315c 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md @@ -2,4 +2,19 @@ uid: Cuemon.AspNetCore.Mvc summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore,Mvc namespace contains types that specializes in cache expiration and validation models and an abundant range of ready-to-use filters in the ASP.NET Core MVC pipeline. The namespace is an addition to the Microsoft.AspNetCore.Mvc namespace. + +Availability: NET Standard 2.0, NET Core 3.0 + +Complements: [Microsoft.AspNetCore.Mvc namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc?view=aspnetcore-2.0) 🔗 + +Related: [Cuemon.Extensions.AspNetCore.Mvc namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Mvc.html) 📘 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Mvc)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Mvc)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Mvc) + +NuGet packages 📦\ +[Cuemon.AspNetCore (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Mvc)\ +[Cuemon.AspNetCore (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Mvc) \ No newline at end of file From dae12381df5d0708ba8d751392b89b90a1b99413 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Sun, 18 Oct 2020 01:27:38 +0200 Subject: [PATCH 341/385] Updated release notes and minor refactoring. --- .../HttpExceptionDescriptor.cs | 2 +- .../Http/Throttling/MemoryThrottlingCache.cs | 13 +++++++++++++ .../Properties/PackageReleaseNotes.txt | 10 ++++++---- 3 files changed, 20 insertions(+), 5 deletions(-) rename src/Cuemon.AspNetCore/{Http => Diagnostics}/HttpExceptionDescriptor.cs (98%) create mode 100644 src/Cuemon.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs diff --git a/src/Cuemon.AspNetCore/Http/HttpExceptionDescriptor.cs b/src/Cuemon.AspNetCore/Diagnostics/HttpExceptionDescriptor.cs similarity index 98% rename from src/Cuemon.AspNetCore/Http/HttpExceptionDescriptor.cs rename to src/Cuemon.AspNetCore/Diagnostics/HttpExceptionDescriptor.cs index 6924e7291..e2433fe23 100644 --- a/src/Cuemon.AspNetCore/Http/HttpExceptionDescriptor.cs +++ b/src/Cuemon.AspNetCore/Diagnostics/HttpExceptionDescriptor.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.WebUtilities; -namespace Cuemon.AspNetCore.Http +namespace Cuemon.AspNetCore.Diagnostics { /// /// Provides information about an , in a developer friendly way, optimized for open- and otherwise public application programming interfaces (API). diff --git a/src/Cuemon.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs b/src/Cuemon.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs new file mode 100644 index 000000000..3701f381b --- /dev/null +++ b/src/Cuemon.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs @@ -0,0 +1,13 @@ +using System.Collections.Concurrent; + +namespace Cuemon.AspNetCore.Http.Throttling +{ + /// + /// Provides a simple in-memory representation of the . This class cannot be inherited. + /// + /// + /// + public sealed class MemoryThrottlingCache : ConcurrentDictionary, IThrottlingCache + { + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt b/src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt index 39a6de32a..3a70079d0 100644 --- a/src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt @@ -5,8 +5,10 @@ Availability: NET Standard 2.0, NET Core 3.0 - Any former extension methods of the Cuemon.AspNetCore namespace was merged into the Cuemon.Extensions.AspNetCore namespace   # Breaking Changes -- +- MOVED HttpExceptionDescriptor class from the Cuemon.AspNetCore.Http namespace to Cuemon.AspNetCore.Diagnostics namespace   -# Improvements -- -  \ No newline at end of file +# New Features +- ADDED IServerTiming interface in the Cuemon.AspNetCore.Diagnostics namespace that represents the Server Timing as per W3C Working Draft 28 July 2020 (https://www.w3.org/TR/2020/WD-server-timing-20200728/) +- ADDED ServerTiming class in the Cuemon.AspNetCore.Diagnostics namespace that provides a default implementation of the IServerTiming interface +- ADDED ServerTimingMetric class in the Cuemon.AspNetCore.Diagnostics namespace that represents a HTTP Server-Timing header field entry to communicate one metric and description for the given request-response cycle +- ADDED RetryConditionScope enum in the Cuemon.AspNetCore.Http.Headers namespace that specifies a set of values defining what value to use with a given HTTP header in regards to a retry condition \ No newline at end of file From ba5c0c030d69b11bcc032af98efef3c82b919f69 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Sun, 18 Oct 2020 01:29:49 +0200 Subject: [PATCH 342/385] Last minute design changes. --- .../Configuration/DynamicCacheBusting.cs | 2 +- .../DynamicCacheBustingOptions.cs | 2 +- .../Cuemon.AspNetCore.Mvc.csproj | 4 +-- .../ExceptionDescriptorResult.cs | 2 +- .../FaultResolverDecoratorExtensions.cs | 1 + .../Diagnostics/FaultDescriptorOptions.cs | 2 +- .../Filters/Diagnostics/FaultResolver.cs | 2 +- .../Filters/Diagnostics/ServerTimingFilter.cs | 1 - .../Headers/UserAgentSentinelFilter.cs | 1 - .../Properties/PackageReleaseNotes.txt | 13 ++++++++- .../JsonConverterCollectionExtensions.cs | 2 +- .../Converters/XmlConverterExtensions.cs | 2 +- .../AssemblyCacheBustingOptions.cs | 1 + .../ServiceCollectionExtensions.cs | 1 + .../Diagnostics/FaultResolverExtensions.cs | 1 + .../ServiceCollectionExtensions.cs | 2 +- .../Hosting/ApplicationBuilderExtensions.cs | 25 +++++++++++++++++ .../Headers}/ApplicationBuilderExtensions.cs | 27 +------------------ .../ApplicationBuilderExtensions.cs | 24 +++++++++++++++++ .../Http/Throttling/MemoryThrottlingCache.cs | 14 ---------- .../Http/FakeHttpContextAccessor.cs | 3 +-- .../HostingEnvironmentMiddlewareTest.cs | 2 +- .../CorrelationIdentifierMiddlewareTest.cs | 4 +-- .../RequestIdentifierMiddlewareTest.cs | 2 +- .../UserAgentSentinelMiddlewareTest.cs | 2 +- .../ThrottlingSentinelMiddlewareTest.cs | 1 - .../Assets/UserSecretsHostFixture.cs | 1 - .../SqlDataManagerTest.cs | 1 - .../ExceptionDescriptorTest.cs | 1 - .../ObjectExtensionsTest.cs | 1 - .../Assets/BoolMiddleware.cs | 1 - 31 files changed, 82 insertions(+), 66 deletions(-) rename src/{Cuemon.Extensions.AspNetCore.Mvc => Cuemon.AspNetCore.Mvc}/Configuration/DynamicCacheBusting.cs (97%) rename src/{Cuemon.Extensions.AspNetCore.Mvc => Cuemon.AspNetCore.Mvc}/Configuration/DynamicCacheBustingOptions.cs (97%) create mode 100644 src/Cuemon.Extensions.AspNetCore/Hosting/ApplicationBuilderExtensions.cs rename src/Cuemon.Extensions.AspNetCore/{Builder => Http/Headers}/ApplicationBuilderExtensions.cs (61%) create mode 100644 src/Cuemon.Extensions.AspNetCore/Http/Throttling/ApplicationBuilderExtensions.cs delete mode 100644 src/Cuemon.Extensions.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs b/src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs similarity index 97% rename from src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs rename to src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs index a81e10f0f..16e8f365c 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs +++ b/src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs @@ -3,7 +3,7 @@ using Cuemon.Configuration; using Microsoft.Extensions.Options; -namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration +namespace Cuemon.AspNetCore.Mvc.Configuration { /// /// Provides cache-busting capabilities on a duration based interval. This class cannot be inherited. diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs b/src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs similarity index 97% rename from src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs rename to src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs index 6936e6d12..6b1f9f8a0 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs +++ b/src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs @@ -1,7 +1,7 @@ using System; using Cuemon.AspNetCore.Configuration; -namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration +namespace Cuemon.AspNetCore.Mvc.Configuration { /// /// Specifies options that is related to operations. diff --git a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj index 2a90364a6..ba8edd4f3 100644 --- a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj +++ b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj @@ -8,8 +8,8 @@ Cuemon.AspNetCore.Mvc Cuemon.AspNetCore.Mvc - The Cuemon.AspNetCore.Mvc namespace contains an abundant range of filters, action results and other features related to the Microsoft.AspNetCore.Mvc namespace. - cacheable-object-factory cacheable-object-result content-based-object-result content-time-based-object-result see-other-result http-cacheable-filter http-entity-tag-header-filter http-last-modified-header-filter fault-descriptor-filter fault-resolver http-request-evidence time-measuring-filter configurable-action-filter + The Cuemon.AspNetCore,Mvc namespace contains types that specializes in cache expiration and validation models and an abundant range of ready-to-use filters in the ASP.NET Core MVC pipeline. The namespace is an addition to the Microsoft.AspNetCore.Mvc namespace. + cacheable-object-factory cacheable-object-result content-based-object-result content-time-based-object-result see-other-result http-cacheable-filter http-entity-tag-header-filter http-last-modified-header-filter fault-descriptor-filter fault-resolver http-request-evidence server-timing-filter user-agent-sentinel-filter throttling-sentinel-filter diff --git a/src/Cuemon.AspNetCore.Mvc/ExceptionDescriptorResult.cs b/src/Cuemon.AspNetCore.Mvc/ExceptionDescriptorResult.cs index f0e5e218c..f65a70015 100644 --- a/src/Cuemon.AspNetCore.Mvc/ExceptionDescriptorResult.cs +++ b/src/Cuemon.AspNetCore.Mvc/ExceptionDescriptorResult.cs @@ -1,5 +1,5 @@ using System; -using Cuemon.AspNetCore.Http; +using Cuemon.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Mvc; namespace Cuemon.AspNetCore.Mvc diff --git a/src/Cuemon.AspNetCore.Mvc/Extensions/Filters/Diagnostics/FaultResolverDecoratorExtensions.cs b/src/Cuemon.AspNetCore.Mvc/Extensions/Filters/Diagnostics/FaultResolverDecoratorExtensions.cs index 71fd7773a..5ebd07f20 100644 --- a/src/Cuemon.AspNetCore.Mvc/Extensions/Filters/Diagnostics/FaultResolverDecoratorExtensions.cs +++ b/src/Cuemon.AspNetCore.Mvc/Extensions/Filters/Diagnostics/FaultResolverDecoratorExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Cuemon.AspNetCore.Diagnostics; using Cuemon.AspNetCore.Http; namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorOptions.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorOptions.cs index a2c02d43e..4011a56d8 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorOptions.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultDescriptorOptions.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Reflection; -using Cuemon.AspNetCore.Http; +using Cuemon.AspNetCore.Diagnostics; using Cuemon.AspNetCore.Http.Headers; using Cuemon.AspNetCore.Http.Throttling; using Microsoft.AspNetCore.Http; diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultResolver.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultResolver.cs index 3279e6b0c..2809302ca 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultResolver.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/FaultResolver.cs @@ -1,5 +1,5 @@ using System; -using Cuemon.AspNetCore.Http; +using Cuemon.AspNetCore.Diagnostics; namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics { diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingFilter.cs index 3f1064be2..70ace5571 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Diagnostics/ServerTimingFilter.cs @@ -10,7 +10,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; -using Microsoft.Extensions.Primitives; namespace Cuemon.AspNetCore.Mvc.Filters.Diagnostics { diff --git a/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs b/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs index 524786919..601fd7a4f 100644 --- a/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs +++ b/src/Cuemon.AspNetCore.Mvc/Filters/Headers/UserAgentSentinelFilter.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using Cuemon.AspNetCore.Http; using Cuemon.AspNetCore.Http.Headers; -using Cuemon.AspNetCore.Infrastructure; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Options; diff --git a/src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt b/src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt index 026dadfe6..58d3de014 100644 --- a/src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt @@ -5,7 +5,18 @@ Availability: NET Standard 2.0, NET Core 3.0 - Any former extension methods of the Cuemon.AspNetCore.Mvc namespace was merged into the Cuemon.Extensions.AspNetCore.Mvc namespace   # Breaking Changes -- REMOVED DefaultJsonSerializerSettings class from the Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json namespace as the default settings is given by JsonFormatterOptions +- MOVED ICacheBusting interface (and related) from the Cuemon.AspNetCore.Mvc.Configuration namespace to Cuemon.AspNetCore.Configuration namespace +- MOVED AssemblyCacheBusting class (and related) from the Cuemon.AspNetCore.Mvc.Configuration namespace to Cuemon.Extensions.AspNetCore.Mvc.Configuration namespace +- MOVED ICacheableObjectResult interface (and related) from the Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace to Cuemon.AspNetCore.Mvc namespace +- RENAMED HttpEntityTagHeader class in the Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace to HttpEntityTagHeaderFilter +- RENAMED HttpLastModifiedHeader class in the Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace to HttpLastModifiedHeaderFilter +- RENAMED TimeMeasureAttribute class in the Cuemon.AspNetCore.Mvc.Filters.Diagnostics namespace to ServerTimingAttribute (including refactoring) +- RENAMED TimeMeasuringFilter class in the Cuemon.AspNetCore.Mvc.Filters.Diagnostics namespace to ServerTimingFilter (including refactoring) +- RENAMED TimeMeasuringOptions class in the Cuemon.AspNetCore.Mvc.Filters.Diagnostics namespace to ServerTimingOptions (including refactoring) +  +# New Features +- ADDED TooManyRequestsObjectResult class in the Cuemon.AspNetCore.Mvc namespace that is an ObjectResult that when executed will produce a Too Many Requests (429) response +- ADDED TooManyRequestsResult class in the Cuemon.AspNetCore.Mvc namespace that is an ActionResult that returns a TooManyRequests (429) response   # Improvements - COMPATIBLE with the changes applied to NET Core 3 in regards to only allowing asynchronous I/O diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs index 520580e17..a580e2e0b 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Cuemon.AspNetCore.Http; +using Cuemon.AspNetCore.Diagnostics; using Cuemon.Diagnostics; using Cuemon.Extensions.Newtonsoft.Json; using Microsoft.Extensions.Primitives; diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs index eb3ec3fea..b38221ddc 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Converters/XmlConverterExtensions.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using Cuemon.AspNetCore.Http; +using Cuemon.AspNetCore.Diagnostics; using Cuemon.Diagnostics; using Cuemon.Extensions.Xml; using Cuemon.Extensions.Xml.Serialization.Converters; diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs index b0edc3031..18116105e 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs @@ -1,5 +1,6 @@ using System.Reflection; using Cuemon.AspNetCore.Configuration; +using Cuemon.AspNetCore.Mvc.Configuration; using Cuemon.Security.Cryptography; namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs index fc90c170d..2ab785ed1 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using Cuemon.AspNetCore.Configuration; +using Cuemon.AspNetCore.Mvc.Configuration; using Microsoft.Extensions.DependencyInjection; namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Diagnostics/FaultResolverExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Diagnostics/FaultResolverExtensions.cs index fa228f67a..beffff694 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Diagnostics/FaultResolverExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Filters/Diagnostics/FaultResolverExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Cuemon.AspNetCore.Diagnostics; using Cuemon.AspNetCore.Http; using Cuemon.AspNetCore.Mvc.Filters.Diagnostics; diff --git a/src/Cuemon.Extensions.AspNetCore/Diagnostics/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Diagnostics/ServiceCollectionExtensions.cs index a2b3d450f..06384cf3e 100644 --- a/src/Cuemon.Extensions.AspNetCore/Diagnostics/ServiceCollectionExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Diagnostics/ServiceCollectionExtensions.cs @@ -1,5 +1,5 @@ using Cuemon.AspNetCore.Diagnostics; -using Cuemon.Extensions.AspNetCore.Http.Throttling; +using Cuemon.AspNetCore.Http.Throttling; using Microsoft.Extensions.DependencyInjection; namespace Cuemon.Extensions.AspNetCore.Diagnostics diff --git a/src/Cuemon.Extensions.AspNetCore/Hosting/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Hosting/ApplicationBuilderExtensions.cs new file mode 100644 index 000000000..34ef1a4c8 --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore/Hosting/ApplicationBuilderExtensions.cs @@ -0,0 +1,25 @@ +using System; +using Cuemon.AspNetCore.Builder; +using Cuemon.AspNetCore.Hosting; +using Microsoft.AspNetCore.Builder; + +namespace Cuemon.Extensions.AspNetCore.Hosting +{ + /// + /// Extension methods for the interface. + /// + public static class ApplicationBuilderExtensions + { + /// + /// Adds a hosting environment HTTP header to the request execution pipeline. + /// + /// The type that provides the mechanisms to configure an application’s request pipeline. + /// The middleware which may be configured. + /// A reference to this instance after the operation has completed. + /// Default HTTP header name is X-Hosting-Environment. + public static IApplicationBuilder UseHostingEnvironment(this IApplicationBuilder builder, Action setup = null) + { + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Http/Headers/ApplicationBuilderExtensions.cs similarity index 61% rename from src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs rename to src/Cuemon.Extensions.AspNetCore/Http/Headers/ApplicationBuilderExtensions.cs index 9056fa9ab..dca7921fe 100644 --- a/src/Cuemon.Extensions.AspNetCore/Builder/ApplicationBuilderExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Http/Headers/ApplicationBuilderExtensions.cs @@ -1,29 +1,15 @@ using System; using Cuemon.AspNetCore.Builder; -using Cuemon.AspNetCore.Hosting; using Cuemon.AspNetCore.Http.Headers; -using Cuemon.AspNetCore.Http.Throttling; using Microsoft.AspNetCore.Builder; -namespace Cuemon.Extensions.AspNetCore.Builder +namespace Cuemon.Extensions.AspNetCore.Http.Headers { /// /// Extension methods for the interface. /// public static class ApplicationBuilderExtensions { - /// - /// Adds a hosting environment HTTP header to the request execution pipeline. - /// - /// The type that provides the mechanisms to configure an application’s request pipeline. - /// The middleware which may be configured. - /// A reference to this instance after the operation has completed. - /// Default HTTP header name is X-Hosting-Environment. - public static IApplicationBuilder UseHostingEnvironment(this IApplicationBuilder builder, Action setup = null) - { - return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); - } - /// /// Adds a correlation identifier HTTP header to the request execution pipeline. /// @@ -58,16 +44,5 @@ public static IApplicationBuilder UseUserAgentSentinel(this IApplicationBuilder { return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); } - - /// - /// Adds a HTTP requests rate limiting / throttling guard to the request execution pipeline. - /// - /// The type that provides the mechanisms to configure an application’s request pipeline. - /// The middleware which may be configured. - /// A reference to this instance after the operation has completed. - public static IApplicationBuilder UseThrottlingSentinel(this IApplicationBuilder builder, Action setup = null) - { - return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); - } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore/Http/Throttling/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Http/Throttling/ApplicationBuilderExtensions.cs new file mode 100644 index 000000000..dc3cab45e --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore/Http/Throttling/ApplicationBuilderExtensions.cs @@ -0,0 +1,24 @@ +using System; +using Cuemon.AspNetCore.Builder; +using Cuemon.AspNetCore.Http.Throttling; +using Microsoft.AspNetCore.Builder; + +namespace Cuemon.Extensions.AspNetCore.Http.Throttling +{ + /// + /// Extension methods for the interface. + /// + public static class ApplicationBuilderExtensions + { + /// + /// Adds a HTTP requests rate limiting / throttling guard to the request execution pipeline. + /// + /// The type that provides the mechanisms to configure an application’s request pipeline. + /// The middleware which may be configured. + /// A reference to this instance after the operation has completed. + public static IApplicationBuilder UseThrottlingSentinel(this IApplicationBuilder builder, Action setup = null) + { + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs b/src/Cuemon.Extensions.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs deleted file mode 100644 index 694fce561..000000000 --- a/src/Cuemon.Extensions.AspNetCore/Http/Throttling/MemoryThrottlingCache.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Concurrent; -using Cuemon.AspNetCore.Http.Throttling; - -namespace Cuemon.Extensions.AspNetCore.Http.Throttling -{ - /// - /// Provides a simple in-memory representation of the . This class cannot be inherited. - /// - /// - /// - public sealed class MemoryThrottlingCache : ConcurrentDictionary, IThrottlingCache - { - } -} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs index b82dc9781..85562bcf8 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs @@ -1,5 +1,4 @@ -using System; -using System.IO; +using System.IO; using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; diff --git a/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs index 23fdf5387..e80357aff 100644 --- a/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Hosting/HostingEnvironmentMiddlewareTest.cs @@ -4,7 +4,7 @@ using System; using System.Linq; using System.Threading.Tasks; -using Cuemon.Extensions.AspNetCore.Builder; +using Cuemon.Extensions.AspNetCore.Hosting; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; using Microsoft.AspNetCore.Builder; using Xunit; diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs index 5b0abccc6..43e68c743 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/CorrelationIdentifierMiddlewareTest.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; -using Cuemon.Extensions.AspNetCore.Builder; +using Cuemon.Extensions.AspNetCore.Http.Headers; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; using Cuemon.Text; using Microsoft.AspNetCore.Builder; @@ -73,4 +73,4 @@ public override void ConfigureApplication(IApplicationBuilder app) app.UseFakeHttpResponseTrigger(); } } -} +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs index d745a20e5..4430bb558 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/RequestIdentifierMiddlewareTest.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; -using Cuemon.Extensions.AspNetCore.Builder; +using Cuemon.Extensions.AspNetCore.Http.Headers; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; using Cuemon.Messaging; using Cuemon.Text; diff --git a/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs index b557792df..fb7bd481c 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Headers/UserAgentSentinelMiddlewareTest.cs @@ -1,6 +1,6 @@ using System.Linq; using System.Threading.Tasks; -using Cuemon.Extensions.AspNetCore.Builder; +using Cuemon.Extensions.AspNetCore.Http.Headers; using Cuemon.Extensions.IO; using Cuemon.Extensions.Xunit; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; diff --git a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs index 1340fcbc1..b8439e02f 100644 --- a/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Tests/Http/Throttling/ThrottlingSentinelMiddlewareTest.cs @@ -1,6 +1,5 @@ using System; using System.Threading.Tasks; -using Cuemon.Extensions.AspNetCore.Builder; using Cuemon.Extensions.AspNetCore.Http.Throttling; using Cuemon.Extensions.IO; using Cuemon.Extensions.Xunit; diff --git a/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs b/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs index fbe622864..15ebb0759 100644 --- a/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs +++ b/test/Cuemon.Data.SqlClient.Tests/Assets/UserSecretsHostFixture.cs @@ -2,7 +2,6 @@ using Cuemon.Extensions.Xunit; using Cuemon.Extensions.Xunit.Hosting; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace Cuemon.Data.SqlClient.Assets diff --git a/test/Cuemon.Data.SqlClient.Tests/SqlDataManagerTest.cs b/test/Cuemon.Data.SqlClient.Tests/SqlDataManagerTest.cs index 563ae9613..4827477b1 100644 --- a/test/Cuemon.Data.SqlClient.Tests/SqlDataManagerTest.cs +++ b/test/Cuemon.Data.SqlClient.Tests/SqlDataManagerTest.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using Cuemon.Data.SqlClient.Assets; diff --git a/test/Cuemon.Diagnostics.Tests/ExceptionDescriptorTest.cs b/test/Cuemon.Diagnostics.Tests/ExceptionDescriptorTest.cs index 1005f7f1a..bb572f18d 100644 --- a/test/Cuemon.Diagnostics.Tests/ExceptionDescriptorTest.cs +++ b/test/Cuemon.Diagnostics.Tests/ExceptionDescriptorTest.cs @@ -4,7 +4,6 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Text; using System.Threading; using Cuemon.Collections.Generic; using Cuemon.Diagnostics.Assets; diff --git a/test/Cuemon.Extensions.Core.Tests/ObjectExtensionsTest.cs b/test/Cuemon.Extensions.Core.Tests/ObjectExtensionsTest.cs index dee949a99..9c1c9b69d 100644 --- a/test/Cuemon.Extensions.Core.Tests/ObjectExtensionsTest.cs +++ b/test/Cuemon.Extensions.Core.Tests/ObjectExtensionsTest.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Linq; using Cuemon.Collections.Generic; using Cuemon.Extensions.Xunit; diff --git a/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs index d35619c7e..59431861d 100644 --- a/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs +++ b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Cuemon.AspNetCore; using Cuemon.AspNetCore.Http.Headers; -using Cuemon.Collections.Generic; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; From 38b032a9a1df3226f3bae824c9d6b0b9158bbe7f Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 19 Oct 2020 01:40:21 +0200 Subject: [PATCH 343/385] Applied minor improvements. --- .../MvcFilterAspNetCoreHostTest.cs | 1 + .../ApplicationBuilderExtensions.cs | 8 ++-- .../Http/Features/FakeHttpResponseFeature.cs | 15 ++++++- .../Features/FakeHttpResponseMiddleware.cs | 35 ++++++++++++--- .../Http/Features/FakeHttpResponseOptions.cs | 44 +++++++++++++++++++ .../MiddlewareAspNetCoreHostTest.cs | 1 + 6 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseOptions.cs diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterAspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterAspNetCoreHostTest.cs index ad1aea612..c8acffc9d 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterAspNetCoreHostTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/MvcFilterAspNetCoreHostTest.cs @@ -23,6 +23,7 @@ internal MvcFilterAspNetCoreHostTest(Action pipelineConfigu Host = hostFixture.Host; ServiceProvider = hostFixture.Host.Services; Application = hostFixture.Application; + Configure(hostFixture.Configuration, hostFixture.HostingEnvironment); } protected override void InitializeHostFixture(AspNetCoreHostFixture hostFixture) diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ApplicationBuilderExtensions.cs index d8ac99df1..6a87ba309 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ApplicationBuilderExtensions.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/ApplicationBuilderExtensions.cs @@ -1,4 +1,5 @@ -using Cuemon.AspNetCore.Builder; +using System; +using Cuemon.AspNetCore.Builder; using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features; using Microsoft.AspNetCore.Builder; @@ -13,11 +14,12 @@ public static class ApplicationBuilderExtensions /// Adds a to the request execution pipeline. /// /// The type that provides the mechanisms to configure an application’s request pipeline. + /// The which may be configured. /// A reference to this instance after the operation has completed. /// - public static IApplicationBuilder UseFakeHttpResponseTrigger(this IApplicationBuilder builder) + public static IApplicationBuilder UseFakeHttpResponseTrigger(this IApplicationBuilder builder, Action setup = null) { - return MiddlewareBuilderFactory.UseMiddleware(builder); + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs index 807afa1a2..48fc0fcea 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs @@ -23,6 +23,12 @@ public class FakeHttpResponseFeature : HttpResponseFeature /// The state to pass into the callback. public override void OnStarting(Func callback, object state) { + if (_hasStarted) { return; } + if (ShortCircuitOnStarting) + { + _hasStarted = true; + callback?.Invoke(state); + } _callback = callback; _state = state; } @@ -41,14 +47,19 @@ public override void OnStarting(Func callback, object state) /// true if this instance has started; otherwise, false. public override bool HasStarted => _hasStarted; + /// + /// Gets or sets a value indicating whether is invoked immediately upon initialization. + /// + /// true if is invoked immediately upon initialization; otherwise, false. + public bool ShortCircuitOnStarting { get; set; } + /// /// Executes the function delegate assigned by . /// /// A task that represents the asynchronous operation. - public Task TriggerOnStarting() + public Task TriggerOnStartingAsync() { _hasStarted = true; - StatusCode = 200; return HasOnStartingCallback ? _callback(_state) : Task.CompletedTask; } } diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseMiddleware.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseMiddleware.cs index abe9f56de..202d77935 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseMiddleware.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseMiddleware.cs @@ -1,7 +1,9 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Cuemon.AspNetCore; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Options; namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features { @@ -9,13 +11,23 @@ namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features /// Provides a fake HTTP response middleware implementation for ASP.NET Core testing. /// /// - public class FakeHttpResponseMiddleware : Middleware + public class FakeHttpResponseMiddleware : ConfigurableMiddleware { /// /// Initializes a new instance of the class. /// /// The delegate of the request pipeline to invoke. - public FakeHttpResponseMiddleware(RequestDelegate next) : base(next) + /// The which need to be configured. + public FakeHttpResponseMiddleware(RequestDelegate next, IOptions setup) : base(next, setup) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + /// The which need to be configured. + public FakeHttpResponseMiddleware(RequestDelegate next, Action setup) : base(next, setup) { } @@ -24,10 +36,21 @@ public FakeHttpResponseMiddleware(RequestDelegate next) : base(next) /// /// The context of the current request. /// A task that represents the execution of this middleware. - public override Task InvokeAsync(HttpContext context) + public override async Task InvokeAsync(HttpContext context) { - var feature = context.Features.Get() as FakeHttpResponseFeature; - return feature?.TriggerOnStarting(); + if (context.Features.Get() is FakeHttpResponseFeature feature) + { + if (Options.ShortCircuitOnStarting) + { + feature.ShortCircuitOnStarting = Options.ShortCircuitOnStarting; + await Next(context).ConfigureAwait(false); + } + else + { + await feature.TriggerOnStartingAsync().ConfigureAwait(false); + } + feature.StatusCode = Options.StatusCode; + } } } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseOptions.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseOptions.cs new file mode 100644 index 000000000..6cf5d076f --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseOptions.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Http; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features +{ + /// + /// Configuration options for . + /// + public class FakeHttpResponseOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// false + /// + /// + /// + public FakeHttpResponseOptions() + { + StatusCode = StatusCodes.Status200OK; + ShortCircuitOnStarting = false; + } + + /// + /// Gets or sets a value indicating whether is invoked immediately upon initialization. + /// + /// true if is invoked immediately upon initialization; otherwise, false. + public bool ShortCircuitOnStarting { get; set; } + + /// + /// Gets or sets the default status code to set in the feature response. + /// + /// The default status code to set in the feature response. + public int StatusCode { get; set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareAspNetCoreHostTest.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareAspNetCoreHostTest.cs index f3887bf6f..eaa266b7e 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareAspNetCoreHostTest.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/MiddlewareAspNetCoreHostTest.cs @@ -23,6 +23,7 @@ internal MiddlewareAspNetCoreHostTest(Action pipelineConfig Host = hostFixture.Host; ServiceProvider = hostFixture.Host.Services; Application = hostFixture.Application; + Configure(hostFixture.Configuration, hostFixture.HostingEnvironment); } protected override void InitializeHostFixture(AspNetCoreHostFixture hostFixture) From 18d6ff24977c74df26cf54d4609e43ea69de51e2 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 19 Oct 2020 01:42:51 +0200 Subject: [PATCH 344/385] Restructuring and appying common convention. --- Cuemon.sln | 26 +++- .../AuthenticationOptions.cs | 26 ++-- .../BasicAuthenticationMiddleware.cs | 36 ++---- .../DigestAccessAuthenticationMiddleware.cs | 46 +++---- .../HttpContextDecoratorExtensions.cs | 20 +++ .../HmacAuthenticationMiddleware.cs | 34 ++---- .../ApplicationBuilderExtensions.cs | 46 +++++++ ...xtensions.AspNetCore.Authentication.csproj | 19 +++ .../Properties/AssemblyInfo.cs | 4 + .../Properties/PackageReleaseNotes.txt | 9 ++ .../BasicAuthenticationMiddlewareTest.cs | 115 ++++++++++++++++++ ...mon.AspNetCore.Authentication.Tests.csproj | 15 +++ 12 files changed, 303 insertions(+), 93 deletions(-) create mode 100644 src/Cuemon.AspNetCore.Authentication/Extensions/HttpContextDecoratorExtensions.cs create mode 100644 src/Cuemon.Extensions.AspNetCore.Authentication/ApplicationBuilderExtensions.cs create mode 100644 src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj create mode 100644 src/Cuemon.Extensions.AspNetCore.Authentication/Properties/AssemblyInfo.cs create mode 100644 src/Cuemon.Extensions.AspNetCore.Authentication/Properties/PackageReleaseNotes.txt create mode 100644 test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs create mode 100644 test/Cuemon.AspNetCore.Authentication.Tests/Cuemon.AspNetCore.Authentication.Tests.csproj diff --git a/Cuemon.sln b/Cuemon.sln index 54adc19d4..1770b9afa 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -131,7 +131,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Hos EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests", "test\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj", "{72422689-CDC3-4AD6-89D7-25F85545B0FE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc", "src\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj", "{193C3DBA-DB3A-40D9-A95B-3102189910E5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc", "src\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc\Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj", "{210BDF91-E7C7-4CB4-A39D-E1A5374C5602}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.AspNetCore.Authentication.Tests", "test\Cuemon.AspNetCore.Authentication.Tests\Cuemon.AspNetCore.Authentication.Tests.csproj", "{D29D6F7E-8A76-48AD-A042-EFC83CD474A5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cuemon.Extensions.AspNetCore.Authentication", "src\Cuemon.Extensions.AspNetCore.Authentication\Cuemon.Extensions.AspNetCore.Authentication.csproj", "{C5DB7806-EC4F-4D74-A9FE-A5B9A0625A41}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -387,10 +391,18 @@ Global {72422689-CDC3-4AD6-89D7-25F85545B0FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {72422689-CDC3-4AD6-89D7-25F85545B0FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {72422689-CDC3-4AD6-89D7-25F85545B0FE}.Release|Any CPU.Build.0 = Release|Any CPU - {193C3DBA-DB3A-40D9-A95B-3102189910E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {193C3DBA-DB3A-40D9-A95B-3102189910E5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {193C3DBA-DB3A-40D9-A95B-3102189910E5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {193C3DBA-DB3A-40D9-A95B-3102189910E5}.Release|Any CPU.Build.0 = Release|Any CPU + {210BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {210BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Debug|Any CPU.Build.0 = Debug|Any CPU + {210BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.ActiveCfg = Release|Any CPU + {210BDF91-E7C7-4CB4-A39D-E1A5374C5602}.Release|Any CPU.Build.0 = Release|Any CPU + {D29D6F7E-8A76-48AD-A042-EFC83CD474A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D29D6F7E-8A76-48AD-A042-EFC83CD474A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D29D6F7E-8A76-48AD-A042-EFC83CD474A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D29D6F7E-8A76-48AD-A042-EFC83CD474A5}.Release|Any CPU.Build.0 = Release|Any CPU + {C5DB7806-EC4F-4D74-A9FE-A5B9A0625A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C5DB7806-EC4F-4D74-A9FE-A5B9A0625A41}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C5DB7806-EC4F-4D74-A9FE-A5B9A0625A41}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C5DB7806-EC4F-4D74-A9FE-A5B9A0625A41}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -458,7 +470,9 @@ Global {A9610C9E-1944-4771-A5E1-CE47ADA243D5} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} {200BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} {72422689-CDC3-4AD6-89D7-25F85545B0FE} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} - {193C3DBA-DB3A-40D9-A95B-3102189910E5} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} + {210BDF91-E7C7-4CB4-A39D-E1A5374C5602} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} + {D29D6F7E-8A76-48AD-A042-EFC83CD474A5} = {31707D2B-843E-4D4F-B9C7-3E74EF8DA338} + {C5DB7806-EC4F-4D74-A9FE-A5B9A0625A41} = {B59C8DF7-7DEC-46AF-A165-CC9E3AD01EA8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2A848386-B682-4F6D-8254-B5F6247C3054} diff --git a/src/Cuemon.AspNetCore.Authentication/AuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/AuthenticationOptions.cs index a2f484b8a..4785d482f 100644 --- a/src/Cuemon.AspNetCore.Authentication/AuthenticationOptions.cs +++ b/src/Cuemon.AspNetCore.Authentication/AuthenticationOptions.cs @@ -1,6 +1,6 @@ using System; -using System.Text; -using Cuemon.Text; +using System.Net; +using System.Net.Http; namespace Cuemon.AspNetCore.Authentication { @@ -14,14 +14,12 @@ public abstract class AuthenticationOptions /// protected AuthenticationOptions() { - HttpNotAuthorizedBody = () => + ResponseHandler = () => new HttpResponseMessage(HttpStatusCode.Unauthorized) { - return Convertible.GetBytes("401 Unauthorized", o => - { - o.Encoding = Encoding.UTF8; - o.Preamble = PreambleSequence.Remove; - }); + Content = new StringContent(UnauthorizedMessage) }; + RequireSecureConnection = true; + UnauthorizedMessage = "The request has not been applied because it lacks valid authentication credentials for the target resource."; } /// @@ -31,9 +29,15 @@ protected AuthenticationOptions() public bool RequireSecureConnection { get; set; } /// - /// Gets or sets the function delegate for retrieving content for the body of an unauthorized request. + /// Gets or sets the function delegate that configures the unauthorized response in the form of a . /// - /// A for retrieving content for the body of an unauthorized request. - public Func HttpNotAuthorizedBody { get; set; } + /// The function delegate that configures the unauthorized response in the form of a . + public Func ResponseHandler { get; set; } + + /// + /// Gets or sets the message of an unauthorized request. + /// + /// The message of an unauthorized request. + public string UnauthorizedMessage { get; set; } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs index 3105a7450..c4b47a186 100644 --- a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs @@ -2,9 +2,8 @@ using System.Security.Claims; using System.Text; using System.Threading.Tasks; -using Cuemon.AspNetCore.Builder; +using Cuemon.IO; using Cuemon.Text; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; @@ -43,12 +42,18 @@ public override async Task InvokeAsync(HttpContext context) { if (!AuthenticationUtility.TryAuthenticate(context, Options.RequireSecureConnection, AuthorizationHeaderParser, TryAuthenticate)) { - context.Response.StatusCode = AuthenticationUtility.HttpNotAuthorizedStatusCode; - context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{AuthenticationScheme} realm=\"{Options.Realm}\"")); - await context.WriteHttpNotAuthorizedBody(Options.HttpNotAuthorizedBody).ConfigureAwait(false); - return; + await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (message, response) => + { + context.Response.OnStarting(() => + { + context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{AuthenticationScheme} realm=\"{Options.Realm}\"")); + return Task.CompletedTask; + }); + response.StatusCode = (int)message.StatusCode; + await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); + }).ConfigureAwait(false); } - await Next.Invoke(context).ConfigureAwait(false); + await Next(context).ConfigureAwait(false); } /// @@ -85,21 +90,4 @@ private Template AuthorizationHeaderParser(HttpContext context, return null; } } - - /// - /// This is a factory implementation of the class. - /// - public static class BasicAuthenticationBuilderExtension - { - /// - /// Adds a HTTP Basic Authentication scheme to the request execution pipeline. - /// - /// The type that provides the mechanisms to configure an application’s request pipeline. - /// The HTTP middleware which need to be configured. - /// A reference to this instance after the operation has completed. - public static IApplicationBuilder UseBasicAuthentication(this IApplicationBuilder builder, Action setup = null) - { - return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); - } - } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs index a1ccb659c..5ead46801 100644 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs @@ -6,8 +6,7 @@ using System.Security.Claims; using System.Threading; using System.Threading.Tasks; -using Cuemon.AspNetCore.Builder; -using Microsoft.AspNetCore.Builder; +using Cuemon.IO; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; @@ -64,16 +63,22 @@ public override async Task InvokeAsync(HttpContext context) { if (!AuthenticationUtility.TryAuthenticate(context, Options.RequireSecureConnection, AuthorizationHeaderParser, TryAuthenticate)) { - context.Response.StatusCode = AuthenticationUtility.HttpNotAuthorizedStatusCode; - string etag = context.Response.Headers[HeaderNames.ETag]; - if (string.IsNullOrEmpty(etag)) { etag = "no-entity-tag"; } - var opaqueGenerator = Options.OpaqueGenerator; - var nonceSecret = Options.NonceSecret; - var nonceGenerator = Options.NonceGenerator; - var staleNonce = context.Items["staleNonce"] as string ?? "FALSE"; - context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{AuthenticationScheme} realm=\"{Options.Realm}\", qop=\"{DigestAuthenticationUtility.CredentialQualityOfProtectionOptions}\", nonce=\"{nonceGenerator(DateTime.UtcNow, etag, nonceSecret())}\", opaque=\"{opaqueGenerator()}\", stale=\"{staleNonce}\", algorithm=\"{DigestAuthenticationUtility.ParseAlgorithm(Options.Algorithm)}\"")); - await context.WriteHttpNotAuthorizedBody(Options.HttpNotAuthorizedBody).ConfigureAwait(false); - return; + await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (message, response) => + { + context.Response.OnStarting(() => + { + string etag = context.Response.Headers[HeaderNames.ETag]; + if (string.IsNullOrEmpty(etag)) { etag = "no-entity-tag"; } + var opaqueGenerator = Options.OpaqueGenerator; + var nonceSecret = Options.NonceSecret; + var nonceGenerator = Options.NonceGenerator; + var staleNonce = context.Items["staleNonce"] as string ?? "FALSE"; + context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{AuthenticationScheme} realm=\"{Options.Realm}\", qop=\"{DigestAuthenticationUtility.CredentialQualityOfProtectionOptions}\", nonce=\"{nonceGenerator(DateTime.UtcNow, etag, nonceSecret())}\", opaque=\"{opaqueGenerator()}\", stale=\"{staleNonce}\", algorithm=\"{DigestAuthenticationUtility.ParseAlgorithm(Options.Algorithm)}\"")); + return Task.CompletedTask; + }); + response.StatusCode = (int)message.StatusCode; + await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); + }).ConfigureAwait(false); } await Next.Invoke(context).ConfigureAwait(false); } @@ -163,21 +168,4 @@ private static bool IsDigestCredentialsValid(string[] credentials) return valid; } } - - /// - /// This is a factory implementation of the class. - /// - public static class DigestAccessAuthenticationBuilderExtension - { - /// - /// Adds a HTTP Digest Authentication scheme to the request execution pipeline. - /// - /// The type that provides the mechanisms to configure an application’s request pipeline. - /// The HTTP middleware which need to be configured. - /// A reference to this instance after the operation has completed. - public static IApplicationBuilder UseDigestAccessAuthentication(this IApplicationBuilder builder, Action setup = null) - { - return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); - } - } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Extensions/HttpContextDecoratorExtensions.cs b/src/Cuemon.AspNetCore.Authentication/Extensions/HttpContextDecoratorExtensions.cs new file mode 100644 index 000000000..d9d5275c3 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Extensions/HttpContextDecoratorExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Cuemon.AspNetCore.Authentication +{ + internal static class HttpContextDecoratorExtensions + { + internal static async Task InvokeAuthenticationAsync(this IDecorator decorator, TOptions options, Action transformer) where TOptions : AuthenticationOptions + { + var message = options.ResponseHandler?.Invoke(); + if (message != null) + { + transformer?.Invoke(message, decorator.Inner.Response); + throw new UnauthorizedException((int)message.StatusCode, await message.Content.ReadAsStringAsync().ConfigureAwait(false)); + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs index 109087602..de363a6b6 100644 --- a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs @@ -2,9 +2,8 @@ using System.Linq; using System.Security.Claims; using System.Threading.Tasks; -using Cuemon.AspNetCore.Builder; +using Cuemon.IO; using Cuemon.Security.Cryptography; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; @@ -43,10 +42,16 @@ public override async Task InvokeAsync(HttpContext context) { if (!AuthenticationUtility.TryAuthenticate(context, Options.RequireSecureConnection, AuthorizationHeaderParser, TryAuthenticate)) { - context.Response.StatusCode = AuthenticationUtility.HttpNotAuthorizedStatusCode; - context.Response.Headers.Add(HeaderNames.WWWAuthenticate, Options.AuthenticationScheme); - await context.WriteHttpNotAuthorizedBody(Options.HttpNotAuthorizedBody).ConfigureAwait(false); - return; + await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (message, response) => + { + context.Response.OnStarting(() => + { + context.Response.Headers.Add(HeaderNames.WWWAuthenticate, Options.AuthenticationScheme); + return Task.CompletedTask; + }); + response.StatusCode = (int)message.StatusCode; + await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); + }).ConfigureAwait(false); } await Next.Invoke(context).ConfigureAwait(false); } @@ -89,21 +94,4 @@ private Template AuthorizationHeaderParser(HttpContext context, return null; } } - - /// - /// This is a factory implementation of the class. - /// - public static class HmacAuthenticationBuilderExtension - { - /// - /// Adds a HTTP HMAC Authentication scheme to the request execution pipeline. - /// - /// The type that provides the mechanisms to configure an application’s request pipeline. - /// The HTTP middleware which need to be configured. - /// A reference to this instance after the operation has completed. - public static IApplicationBuilder UseHmacAuthentication(this IApplicationBuilder builder, Action setup = null) - { - return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); - } - } } \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Authentication/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Authentication/ApplicationBuilderExtensions.cs new file mode 100644 index 000000000..d4ede1a03 --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Authentication/ApplicationBuilderExtensions.cs @@ -0,0 +1,46 @@ +using System; +using Cuemon.AspNetCore.Authentication; +using Cuemon.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; + +namespace Cuemon.Extensions.AspNetCore.Authentication +{ + /// + /// Extension methods for the interface. + /// + public static class ApplicationBuilderExtensions + { + /// + /// Adds a HTTP Basic Authentication scheme to the request execution pipeline. + /// + /// The type that provides the mechanisms to configure an application’s request pipeline. + /// The HTTP middleware which may be configured. + /// A reference to after the operation has completed. + public static IApplicationBuilder UseBasicAuthentication(this IApplicationBuilder builder, Action setup = null) + { + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); + } + + /// + /// Adds a HTTP Digest Authentication scheme to the request execution pipeline. + /// + /// The type that provides the mechanisms to configure an application’s request pipeline. + /// The HTTP middleware which may be configured. + /// A reference to after the operation has completed. + public static IApplicationBuilder UseDigestAccessAuthentication(this IApplicationBuilder builder, Action setup = null) + { + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); + } + + /// + /// Adds a HTTP HMAC Authentication scheme to the request execution pipeline. + /// + /// The type that provides the mechanisms to configure an application’s request pipeline. + /// The HTTP middleware which may be configured. + /// A reference to after the operation has completed. + public static IApplicationBuilder UseHmacAuthentication(this IApplicationBuilder builder, Action setup = null) + { + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj b/src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj new file mode 100644 index 000000000..bc01c7f62 --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + 220bdf91-e7c7-4cb4-a39d-e1a5374c5602 + + + + Cuemon.Extensions.AspNetCore.Authentication + Cuemon.Extensions.AspNetCore.Authentication + The Cuemon.Extensions.AspNetCore.Authentication namespace contains both types and extension methods that complements the Cuemon.AspNetCore namespace while being an addition to the Microsoft.AspNetCore namespace. Provides an in-memory implementation of a throttling cache for ASP.NET Core. + extension-methods extensions memory-throttling-cache use-hosting-environment-header use-correlation-identifier-header use-request-identifier-header use-user-agent-sentinel use-custom-throttling-sentinel + + + + + + + \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Authentication/Properties/AssemblyInfo.cs b/src/Cuemon.Extensions.AspNetCore.Authentication/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..354bcf016 --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Authentication/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("5e2ba4d0-1e7d-4ea1-9ca8-9133d7fa2319")] \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Authentication/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.AspNetCore.Authentication/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..9dd2cf1b0 --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Authentication/Properties/PackageReleaseNotes.txt @@ -0,0 +1,9 @@ +Version: 6.0.0 +Availability: NET Standard 2.0 +  +# Breaking Changes +- +  +# New Features +- +  \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs b/test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs new file mode 100644 index 000000000..998c03efd --- /dev/null +++ b/test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs @@ -0,0 +1,115 @@ +using System.Security.Claims; +using System.Threading.Tasks; +using Cuemon.Collections.Generic; +using Cuemon.Extensions.AspNetCore.Authentication; +using Cuemon.Extensions.Xunit; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; +using Xunit; +using Xunit.Abstractions; +using Cuemon.Extensions; + +namespace Cuemon.AspNetCore.Authentication +{ + public class BasicAuthenticationMiddlewareTest : Test + { + public BasicAuthenticationMiddlewareTest(ITestOutputHelper output) : base(output) + { + } + + + [Fact] + public async Task InvokeAsync_ShouldNotBeAuthenticated() + { + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseFakeHttpResponseTrigger(o => o.ShortCircuitOnStarting = true); + app.UseBasicAuthentication(); + }, services => + { + services.Configure(o => + { + o.Authenticator = (username, password) => + { + return null; + }; + o.RequireSecureConnection = false; + }); + services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + + var wwwAuthenticate = context.Response.Headers[HeaderNames.WWWAuthenticate]; + + TestOutput.WriteLine(wwwAuthenticate); + + var encodedUsernameAndPassword = "Agent:Test".ToByteArray().ToBase64String(); + context.Request.Headers.Add(HeaderNames.Authorization, $"Basic {encodedUsernameAndPassword}"); + + ue = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + } + } + + [Fact] + public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeader() + { + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseFakeHttpResponseTrigger(o => o.ShortCircuitOnStarting = true); + app.UseBasicAuthentication(); + }, services => + { + services.Configure(o => + { + o.Authenticator = (username, password) => + { + if (username == "Agent" && password == "Test") + { + var cp = new ClaimsPrincipal(); + cp.AddIdentity(new ClaimsIdentity(Arguments.Yield(new Claim("Name", "Test Agent")))); + return cp; + } + return null; + }; + o.RequireSecureConnection = false; + }); + services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + + var wwwAuthenticate = context.Response.Headers[HeaderNames.WWWAuthenticate]; + + TestOutput.WriteLine(wwwAuthenticate); + + var encodedUsernameAndPassword = "Agent:Test".ToByteArray().ToBase64String(); + context.Request.Headers.Add(HeaderNames.Authorization, $"Basic {encodedUsernameAndPassword}"); + + await pipeline(context); + + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } + } + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Authentication.Tests/Cuemon.AspNetCore.Authentication.Tests.csproj b/test/Cuemon.AspNetCore.Authentication.Tests/Cuemon.AspNetCore.Authentication.Tests.csproj new file mode 100644 index 000000000..b377df6c5 --- /dev/null +++ b/test/Cuemon.AspNetCore.Authentication.Tests/Cuemon.AspNetCore.Authentication.Tests.csproj @@ -0,0 +1,15 @@ + + + + Cuemon.AspNetCore.Authentication + + + + + + + + + + + \ No newline at end of file From 45394fa658def1ddfed4d1e57a204e5ba68040ed Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 19 Oct 2020 01:43:08 +0200 Subject: [PATCH 345/385] Removed last InternalsVisibleTo. --- src/Cuemon.Extensions.AspNetCore/Properties/AssemblyInfo.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Cuemon.Extensions.AspNetCore/Properties/AssemblyInfo.cs b/src/Cuemon.Extensions.AspNetCore/Properties/AssemblyInfo.cs index f6682d1b8..3ffaab395 100644 --- a/src/Cuemon.Extensions.AspNetCore/Properties/AssemblyInfo.cs +++ b/src/Cuemon.Extensions.AspNetCore/Properties/AssemblyInfo.cs @@ -1,6 +1,4 @@ -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; [assembly: ComVisible(false)] -[assembly: Guid("51e5cc05-3039-437a-8bdf-f489e9986921")] -[assembly: InternalsVisibleTo("Cuemon.Extensions.AspNetCore.Mvc, PublicKey=00240000048000009400000006020000002400005253413100040000010001002F66D8473F676F4E7B47400527D33951A774422DFFC3DF6D7F87C82E5694E9F3AA626D36BEBEA428AD5B800EFCF6CE87B73268F5A0125A7D38739D344703A1C48785AC1A45B1C27EDFDF2EB30BA2B3E3CEA92E5981C30F3A95685A680B7EBEE66F422D176CD1623019D5A05770B9BA498144B1134593BEA6F674F334CF2B90B0")] \ No newline at end of file +[assembly: Guid("51e5cc05-3039-437a-8bdf-f489e9986921")] \ No newline at end of file From 80124db3a0da694050eb3cb2182400980ff5bd52 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 19 Oct 2020 01:44:32 +0200 Subject: [PATCH 346/385] Minor adjustments. --- .../Extensions/Http/HttpContextDecoratorExtensions.cs | 2 +- .../Http/Headers/UserAgentSentinelOptions.cs | 10 +++++----- src/Cuemon.Core/Disposable.cs | 2 -- .../Http/Throttling/ApplicationBuilderExtensions.cs | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs b/src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs index 442e0b7be..2330b8157 100644 --- a/src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs +++ b/src/Cuemon.AspNetCore/Extensions/Http/HttpContextDecoratorExtensions.cs @@ -96,7 +96,7 @@ public static async Task InvokeUserAgentSentinelAsync(this IDecoratorfalse /// /// - /// + /// /// A initialized to either a HTTP status code 400 or 403 and a body of either or . /// /// @@ -56,7 +56,7 @@ public UserAgentSentinelOptions() BadRequestMessage = "The requirements of the HTTP User-Agent header was not met."; ForbiddenMessage = "The HTTP User-Agent specified was rejected."; AllowedUserAgents = new List(); - ResponseBroker = userAgent => + ResponseHandler = userAgent => { var userAgentIsNullOrWhiteSpace = string.IsNullOrWhiteSpace(userAgent); var forbidden = !userAgentIsNullOrWhiteSpace && @@ -88,12 +88,12 @@ public UserAgentSentinelOptions() /// Gets or sets the function delegate that configures the response in the form of a . /// /// The function delegate that configures the response in the form of a . - public Func ResponseBroker { get; set; } + public Func ResponseHandler { get; set; } /// - /// Gets or sets a value indicating whether the produced should be as neutral as possible. + /// Gets or sets a value indicating whether the produced should be as neutral as possible. /// - /// true if the produced should be as neutral as possible; otherwise, false. + /// true if the produced should be as neutral as possible; otherwise, false. public bool UseGenericResponse { get; set; } /// diff --git a/src/Cuemon.Core/Disposable.cs b/src/Cuemon.Core/Disposable.cs index 30b797125..49c800546 100644 --- a/src/Cuemon.Core/Disposable.cs +++ b/src/Cuemon.Core/Disposable.cs @@ -1,6 +1,4 @@ using System; -using System.Threading; -using System.Threading.Tasks; namespace Cuemon { diff --git a/src/Cuemon.Extensions.AspNetCore/Http/Throttling/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Http/Throttling/ApplicationBuilderExtensions.cs index dc3cab45e..d47730c00 100644 --- a/src/Cuemon.Extensions.AspNetCore/Http/Throttling/ApplicationBuilderExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Http/Throttling/ApplicationBuilderExtensions.cs @@ -15,7 +15,7 @@ public static class ApplicationBuilderExtensions /// /// The type that provides the mechanisms to configure an application’s request pipeline. /// The middleware which may be configured. - /// A reference to this instance after the operation has completed. + /// A reference to after the operation has completed. public static IApplicationBuilder UseThrottlingSentinel(this IApplicationBuilder builder, Action setup = null) { return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); From 479fccf38922ade0790cb4b786161de8ddf2d22b Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 19 Oct 2020 03:55:53 +0200 Subject: [PATCH 347/385] Updated to include NET Core App 3.0. --- azure-pipelines.yml | 1 + docfx/docfx.json | 1 + .../Cuemon.Extensions.AspNetCore.Authentication.csproj | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d66d652f8..4c60f41e6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -150,6 +150,7 @@ jobs: src/**/Cuemon.Data.SqlClient.csproj src/**/Cuemon.Diagnostics.csproj src/**/Cuemon.Extensions.AspNetCore.csproj + src/**/Cuemon.Extensions.AspNetCore.Authentication.csproj src/**/Cuemon.Extensions.AspNetCore.Mvc.csproj src/**/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj src/**/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj diff --git a/docfx/docfx.json b/docfx/docfx.json index ac6b4f2ef..01a3eb0b0 100644 --- a/docfx/docfx.json +++ b/docfx/docfx.json @@ -82,6 +82,7 @@ { "files": [ "Cuemon.Extensions.AspNetCore/**.csproj", + "Cuemon.Extensions.AspNetCore.Authentication/**.csproj", "Cuemon.Extensions.AspNetCore.Mvc/**.csproj", "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/**.csproj", "Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/**.csproj" diff --git a/src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj b/src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj index bc01c7f62..d13b93031 100644 --- a/src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj @@ -1,7 +1,7 @@ - + - netstandard2.0 + netcoreapp3.0;netstandard2.0 220bdf91-e7c7-4cb4-a39d-e1a5374c5602 From 8288f94a65e637d2d98f81ecaa9244d687a91a14 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 22 Oct 2020 00:49:10 +0200 Subject: [PATCH 348/385] Cleanup and alignment. --- .../AuthenticationOptions.cs | 26 +++++++++++--- .../AuthenticationUtility.cs | 35 ------------------- .../BasicAuthenticationMiddleware.cs | 2 +- 3 files changed, 23 insertions(+), 40 deletions(-) diff --git a/src/Cuemon.AspNetCore.Authentication/AuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/AuthenticationOptions.cs index 4785d482f..b7bb2fb83 100644 --- a/src/Cuemon.AspNetCore.Authentication/AuthenticationOptions.cs +++ b/src/Cuemon.AspNetCore.Authentication/AuthenticationOptions.cs @@ -12,12 +12,30 @@ public abstract class AuthenticationOptions /// /// Initializes a new instance of the class. /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// () => new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent(UnauthorizedMessage) }; + /// + /// + /// + /// true + /// + /// + /// + /// The request has not been applied because it lacks valid authentication credentials for the target resource. + /// + /// + /// protected AuthenticationOptions() { - ResponseHandler = () => new HttpResponseMessage(HttpStatusCode.Unauthorized) - { - Content = new StringContent(UnauthorizedMessage) - }; + ResponseHandler = () => new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent(UnauthorizedMessage) }; RequireSecureConnection = true; UnauthorizedMessage = "The request has not been applied because it lacks valid authentication credentials for the target resource."; } diff --git a/src/Cuemon.AspNetCore.Authentication/AuthenticationUtility.cs b/src/Cuemon.AspNetCore.Authentication/AuthenticationUtility.cs index 1447cf51e..931f4e2a0 100644 --- a/src/Cuemon.AspNetCore.Authentication/AuthenticationUtility.cs +++ b/src/Cuemon.AspNetCore.Authentication/AuthenticationUtility.cs @@ -1,7 +1,6 @@ using System; using System.Security; using System.Security.Claims; -using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Net.Http.Headers; @@ -12,31 +11,6 @@ namespace Cuemon.AspNetCore.Authentication /// public static class AuthenticationUtility { - /// - /// The value of the header credential separator of a HTTP Basic access authentication. - /// - public const char BasicAuthenticationCredentialSeparator = ':'; - - /// - /// The value of the header credential separator of a HTTP Digest access authentication. - /// - public const char DigestAuthenticationCredentialSeparator = ','; - - /// - /// The value of the header credential separator of a HTTP JSON Web Token authentication. - /// - public const char JwtAuthenticationCredentialSeparator = '.'; - - /// - /// The value of the status description associated with . - /// - public const string HttpNotAuthorizedStatus = "401 Unauthorized"; - - /// - /// Equivalent to HTTP status 401. Unauthorized indicates that the requested resource requires authentication. - /// - public const int HttpNotAuthorizedStatusCode = 401; - /// /// Provides a generic way to make authentication requests using the specified . /// @@ -97,14 +71,5 @@ internal static bool IsAuthenticationSchemeValid(string authorizationHeader, str { return (!string.IsNullOrEmpty(authorizationHeader) && authorizationHeader.StartsWith(authenticationSchemeName, StringComparison.Ordinal)); } - - internal static async Task WriteHttpNotAuthorizedBody(this HttpContext context, Func httpNotAuthorizedBody) - { - var bodyContent = httpNotAuthorizedBody?.Invoke(); - if (bodyContent != null) - { - await context.Response.Body.WriteAsync(bodyContent, 0, bodyContent.Length).ConfigureAwait(false); - } - } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs index c4b47a186..08781f885 100644 --- a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs @@ -80,7 +80,7 @@ private Template AuthorizationHeaderParser(HttpContext context, { options.Encoding = Encoding.ASCII; options.Preamble = PreambleSequence.Remove; - }).Split(AuthenticationUtility.BasicAuthenticationCredentialSeparator); + }).Split(':'); if (credentials.Length == 2 && !string.IsNullOrEmpty(credentials[0]) && !string.IsNullOrEmpty(credentials[1])) From 43169c1a24ec452204e717605767a7a3eb8113ca Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 22 Oct 2020 00:50:45 +0200 Subject: [PATCH 349/385] Refactored and optimized the HTTP Digest Access Authentication implementation. --- .../DigestAccessAuthenticationMiddleware.cs | 111 +++---- .../DigestAccessAuthenticationOptions.cs | 93 +++++- .../DigestAccessAuthenticationParameters.cs | 20 +- .../DigestAuthenticationUtility.cs | 205 ------------ .../DigestHeaderBuilder.cs | 277 ++++++++++++++++ .../DigestHeaders.cs | 58 ++++ .../GlobalSuppressions.cs | 8 + .../INonceTracker.cs | 34 ++ .../NonceTracker.cs | 101 ++++++ .../NonceTrackerEntry.cs | 33 ++ .../ServiceCollectionExtensions.cs | 23 ++ ...igestAccessAuthenticationMiddlewareTest.cs | 306 ++++++++++++++++++ 12 files changed, 976 insertions(+), 293 deletions(-) delete mode 100644 src/Cuemon.AspNetCore.Authentication/DigestAuthenticationUtility.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/DigestHeaderBuilder.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/DigestHeaders.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/GlobalSuppressions.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/INonceTracker.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/NonceTracker.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/NonceTrackerEntry.cs create mode 100644 src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs create mode 100644 test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs index 5ead46801..23957b727 100644 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs @@ -1,12 +1,9 @@ using System; -using System.Collections.Concurrent; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Security.Claims; -using System.Threading; using System.Threading.Tasks; using Cuemon.IO; +using Cuemon.Security.Cryptography; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; @@ -16,10 +13,9 @@ namespace Cuemon.AspNetCore.Authentication /// /// Provides a HTTP Digest Access Authentication middleware implementation for ASP.NET Core. /// - public class DigestAccessAuthenticationMiddleware : ConfigurableMiddleware + public class DigestAccessAuthenticationMiddleware : ConfigurableMiddleware { - private static readonly ConcurrentDictionary> NonceCounter = new ConcurrentDictionary>(); - private static Timer _nonceCounterSweeper; + private INonceTracker _nonceTracker; /// /// Initializes a new instance of the class. @@ -28,7 +24,6 @@ public class DigestAccessAuthenticationMiddleware : ConfigurableMiddlewareThe which need to be configured. public DigestAccessAuthenticationMiddleware(RequestDelegate next, IOptions setup) : base(next, setup) { - InitializeNonceCounterSweeper(); } /// @@ -38,29 +33,17 @@ public DigestAccessAuthenticationMiddleware(RequestDelegate next, IOptionsThe middleware which need to be configured. public DigestAccessAuthenticationMiddleware(RequestDelegate next, Action setup) : base(next, setup) { - InitializeNonceCounterSweeper(); - } - - private static void InitializeNonceCounterSweeper() - { - _nonceCounterSweeper = new Timer(s => - { - var utcStaleTimestamp = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(5)); - var staledEntries = NonceCounter.Where(pair => pair.Value.Arg1 <= utcStaleTimestamp).ToList(); - foreach (var staledEntry in staledEntries) - { - NonceCounter.TryRemove(staledEntry.Key, out _); - } - }, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(2)); } /// /// Executes the . /// /// The context of the current request. + /// The dependency injected implementation of an . /// A task that represents the execution of this middleware. - public override async Task InvokeAsync(HttpContext context) + public override async Task InvokeAsync(HttpContext context, INonceTracker nonceTracker) { + _nonceTracker = nonceTracker; if (!AuthenticationUtility.TryAuthenticate(context, Options.RequireSecureConnection, AuthorizationHeaderParser, TryAuthenticate)) { await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (message, response) => @@ -72,8 +55,8 @@ await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (messa var opaqueGenerator = Options.OpaqueGenerator; var nonceSecret = Options.NonceSecret; var nonceGenerator = Options.NonceGenerator; - var staleNonce = context.Items["staleNonce"] as string ?? "FALSE"; - context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{AuthenticationScheme} realm=\"{Options.Realm}\", qop=\"{DigestAuthenticationUtility.CredentialQualityOfProtectionOptions}\", nonce=\"{nonceGenerator(DateTime.UtcNow, etag, nonceSecret())}\", opaque=\"{opaqueGenerator()}\", stale=\"{staleNonce}\", algorithm=\"{DigestAuthenticationUtility.ParseAlgorithm(Options.Algorithm)}\"")); + var staleNonce = context.Items["staleNonce"] as string ?? "false"; + context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{AuthenticationScheme} realm=\"{Options.Realm}\", qop=\"auth, auth-int\", nonce=\"{nonceGenerator(DateTime.UtcNow, etag, nonceSecret())}\", opaque=\"{opaqueGenerator()}\", stale=\"{staleNonce}\", algorithm=\"{ParseAlgorithm(Options.Algorithm)}\"")); return Task.CompletedTask; }); response.StatusCode = (int)message.StatusCode; @@ -89,13 +72,13 @@ await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (messa /// The name of the authentication scheme. public string AuthenticationScheme => "Digest"; - private bool TryAuthenticate(HttpContext context, Dictionary credentials, out ClaimsPrincipal result) + private bool TryAuthenticate(HttpContext context, ImmutableDictionary credentials, out ClaimsPrincipal result) { if (Options.Authenticator == null) { throw new InvalidOperationException(FormattableString.Invariant($"The {nameof(Options.Authenticator)} delegate cannot be null.")); } - credentials.TryGetValue(DigestAuthenticationUtility.CredentialUserName, out var userName); - credentials.TryGetValue(DigestAuthenticationUtility.CredentialResponse, out var clientResponse); - credentials.TryGetValue(DigestAuthenticationUtility.CredentialNonceCount, out var nonceCount); - if (credentials.TryGetValue(DigestAuthenticationUtility.CredentialNonce, out var nonce)) + credentials.TryGetValue(DigestHeaders.UserName, out var userName); + credentials.TryGetValue(DigestHeaders.Response, out var clientResponse); + credentials.TryGetValue(DigestHeaders.NonceCount, out var nonceCount); + if (credentials.TryGetValue(DigestHeaders.Nonce, out var nonce)) { result = null; var nonceExpiredParser = Options.NonceExpiredParser; @@ -103,42 +86,33 @@ private bool TryAuthenticate(HttpContext context, Dictionary cre context.Items["staleNonce"] = staleNonce.ToString().ToUpperInvariant(); if (staleNonce) { return false; } - if (NonceCounter.TryGetValue(nonce, out var previousNonce)) + if (_nonceTracker != null) { - if (previousNonce.Arg2.Equals(nonceCount, StringComparison.Ordinal)) { return false; } - } - else - { - NonceCounter.TryAdd(nonce, Template.CreateTwo(DateTime.UtcNow, nonceCount)); + var nc = Convert.ToInt32(nonceCount, 16); + if (_nonceTracker.TryGetEntry(nonce, out var previousNonce)) + { + if (previousNonce.Count == nc) { return false; } + } + else + { + _nonceTracker.TryAddEntry(nonce, nc); + } } } result = Options.Authenticator(userName, out var password); - - var serverResponse = Options?.DigestAccessSigner(new DigestAccessAuthenticationParameters(credentials.ToImmutableDictionary(), context.Request.Method, password, Options.Algorithm)); - return serverResponse != null && StringFactory.CreateHexadecimal(serverResponse).Equals(clientResponse, StringComparison.Ordinal) && Condition.IsNotNull(result); + var serverResponse = Options?.DigestAccessSigner(new DigestAccessAuthenticationParameters(credentials, context.Request.Method, password, Decorator.Enclose(context.Response.Body).ToEncodedString(o => o.LeaveOpen = true), Options.Algorithm)); + return serverResponse != null && serverResponse.Equals(clientResponse, StringComparison.Ordinal) && Condition.IsNotNull(result); } - private Dictionary AuthorizationHeaderParser(HttpContext context, string authorizationHeader) + internal ImmutableDictionary AuthorizationHeaderParser(HttpContext context, string authorizationHeader) { - if (AuthenticationUtility.IsAuthenticationSchemeValid(authorizationHeader, AuthenticationScheme)) - { - var digestCredentials = authorizationHeader.Remove(0, AuthenticationScheme.Length + 1); - var credentials = digestCredentials.Split(AuthenticationUtility.DigestAuthenticationCredentialSeparator); - if (IsDigestCredentialsValid(credentials)) - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - for (var i = 0; i < credentials.Length; i++) - { - var credentialPair = DelimitedString.Split(credentials[i], o => o.Qualifier = "="); - result.Add(credentialPair[0].Trim(), QuotedStringParser(credentialPair[1])); - } - return IsDigestCredentialsValid(result) ? result : null; - } - } - return new Dictionary(); + var id = new DigestHeaderBuilder(Options.Algorithm) + .AddFromDigestHeader(authorizationHeader) + .ToImmutableDictionary(); + return IsDigestCredentialsValid(id) ? id : null; } - private static bool IsDigestCredentialsValid(Dictionary credentials) + private static bool IsDigestCredentialsValid(ImmutableDictionary credentials) { var valid = credentials.ContainsKey("username"); valid |= credentials.ContainsKey("realm"); @@ -148,24 +122,17 @@ private static bool IsDigestCredentialsValid(Dictionary credenti return valid; } - private string QuotedStringParser(string value) + private static string ParseAlgorithm(UnkeyedCryptoAlgorithm algorithm) { - if (value.StartsWith("\"", StringComparison.OrdinalIgnoreCase) && - value.EndsWith("\"", StringComparison.OrdinalIgnoreCase)) + switch (algorithm) { - value = value.Trim('"'); + case UnkeyedCryptoAlgorithm.Sha256: + return "SHA-256"; + case UnkeyedCryptoAlgorithm.Sha512: + return "SHA-512-256"; + default: + return "MD5"; } - return value.Trim(); - } - - private static bool IsDigestCredentialsValid(string[] credentials) - { - var valid = (credentials.Length >= 5 && credentials.Length <= 10); - for (var i = 0; i < credentials.Length; i++) - { - valid |= !string.IsNullOrEmpty(credentials[i]); - } - return valid; } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs index 3b3bf66c8..c079c5e42 100644 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs +++ b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs @@ -1,5 +1,8 @@ using System; +using System.Globalization; +using System.Text; using Cuemon.Security.Cryptography; +using Cuemon.Text; namespace Cuemon.AspNetCore.Authentication { @@ -12,19 +15,88 @@ public sealed class DigestAccessAuthenticationOptions : AuthenticationOptions /// /// Initializes a new instance of the class. /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// + /// + /// + /// + /// null + /// + /// + /// + /// A default implementation of a nonce generator. + /// + /// + /// + /// A default implementation of an opaque generator. + /// + /// + /// + /// A default implementation of a nonce expiry parser. + /// + /// + /// + /// A default secret to get you started without overwhelming configuration. Do change when moving outside a development environment. + /// + /// + /// + /// A default implementation of HTTP Digest Access RESPONSE. + /// + /// + /// + /// AuthenticationServer + /// + /// + /// public DigestAccessAuthenticationOptions() { - Algorithm = UnkeyedCryptoAlgorithm.Md5; - OpaqueGenerator = DigestAuthenticationUtility.DefaultOpaqueGenerator; - NonceExpiredParser = DigestAuthenticationUtility.DefaultNonceExpiredParser; - NonceGenerator = DigestAuthenticationUtility.DefaultNonceGenerator; - NonceSecret = () => DigestAuthenticationUtility.DefaultPrivateKey; + Algorithm = UnkeyedCryptoAlgorithm.Sha256; + OpaqueGenerator = () => Generate.RandomString(32, Alphanumeric.Hexadecimal).ToLowerInvariant(); + NonceExpiredParser = (nonce, timeToLive) => + { + Validator.ThrowIfNullOrEmpty(nonce, nameof(nonce)); + if (ParserFactory.FromBase64().TryParse(nonce, out var rawNonce)) + { + var nonceProtocol = Convertible.ToString(rawNonce, options => + { + options.Encoding = Encoding.UTF8; + options.Preamble = PreambleSequence.Remove; + }); + var nonceTimestamp = DateTime.ParseExact(nonceProtocol.Substring(0, nonceProtocol.LastIndexOf(':')), "u", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); + var difference = (DateTime.UtcNow - nonceTimestamp); + return (difference > timeToLive); + } + return false; + }; + NonceGenerator = (timestamp, entityTag, privateKey) => + { + Validator.ThrowIfNullOrWhitespace(entityTag, nameof(entityTag)); + Validator.ThrowIfNull(privateKey, nameof(privateKey)); + var nonceHash = UnkeyedHashFactory.CreateCryptoSha256().ComputeHash(timestamp.Ticks, entityTag, Convert.ToBase64String(privateKey)).ToHexadecimalString(); + var nonceProtocol = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", timestamp.ToString("u", CultureInfo.InvariantCulture), nonceHash); + return Convert.ToBase64String(Convertible.GetBytes(nonceProtocol, options => + { + options.Encoding = Encoding.UTF8; + options.Preamble = PreambleSequence.Remove; + })); + }; + NonceSecret = () => Convert.FromBase64String("ZHBGWDRrVGVxbFlhVEpWQ3hoYUc5VUlZM05penNOaUk="); DigestAccessSigner = parameters => { - var ha1 = DigestAuthenticationUtility.ComputeHash1(parameters.Credentials, parameters.Password, parameters.Algorithm); - var ha2 = DigestAuthenticationUtility.ComputeHash2(parameters.Credentials, parameters.HttpMethod, parameters.Algorithm); - return DigestAuthenticationUtility.ComputeResponse(parameters.Credentials, ha1, ha2, parameters.Algorithm); + var db = new DigestHeaderBuilder(parameters.Algorithm, parameters.Credentials); + var ha1 = db.ComputeHash1(parameters.Password); + var ha2 = db.ComputeHash2(parameters.Method, parameters.EntityBody); + return db.ComputeResponse(ha1, ha2); }; + Realm = "AuthenticationServer"; } /// @@ -37,12 +109,13 @@ public DigestAccessAuthenticationOptions() /// Gets or sets the function delegate that will sign a message retrieved from a HTTP request. /// /// The function delegate that will sign a message. - public Func DigestAccessSigner { get; set; } + public Func DigestAccessSigner { get; set; } /// - /// Gets or sets the algorithm of the HTTP Digest Access Authentication. Default is . + /// Gets or sets the algorithm of the HTTP Digest Access Authentication. Default is . /// /// The algorithm of the HTTP Digest Access Authentication. + /// Allowed values are: , and . public UnkeyedCryptoAlgorithm Algorithm { get; set; } /// diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs index 9915e7c14..425f05a16 100644 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs +++ b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs @@ -12,28 +12,30 @@ public class DigestAccessAuthenticationParameters /// Initializes a new instance of the class. /// /// The credentials used in the computation of HA1-, HA2-, and response hash values. - /// The HTTP method to include in the HA2 computed value. + /// The HTTP method to include in the HA2 computed value. /// The password to include in the HA1 computed value. + /// The entity body to apply in the signature when qop is set to auth-int. /// The algorithm to use when computing the HA1-, HA2-, and response hash values. - internal DigestAccessAuthenticationParameters(ImmutableDictionary credentials, string httpMethod, string password, UnkeyedCryptoAlgorithm algorithm) + internal DigestAccessAuthenticationParameters(ImmutableDictionary credentials, string method, string password, string entityBody, UnkeyedCryptoAlgorithm algorithm) { Credentials = credentials; - HttpMethod = httpMethod; + Method = method; Password = password; + EntityBody = entityBody; Algorithm = algorithm; } /// - /// Gets the credentials used in the computation of HA1-, HA2-, and response hash values. + /// Gets the credentials used in the computation of HA1-, HA2-, and RESPONSE hash values. /// - /// The credentials used in the computation of HA1-, HA2-, and response hash values. + /// The credentials used in the computation of HA1-, HA2-, and RESPONSE hash values. public ImmutableDictionary Credentials { get; } /// /// Gets the HTTP method to include in the HA2 computed value. /// /// The HTTP method to include in the HA2 computed value. - public string HttpMethod { get; } + public string Method { get; } /// /// Gets the password to include in the HA1 computed value. @@ -46,5 +48,11 @@ internal DigestAccessAuthenticationParameters(ImmutableDictionary /// The algorithm to use when computing the HA1-, HA2-, and response hash values. public UnkeyedCryptoAlgorithm Algorithm { get; } + + /// + /// Gets the entity body to include in the HA2 computed value. + /// + /// The entity body to include in the HA2 computed value. + public string EntityBody { get; } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAuthenticationUtility.cs b/src/Cuemon.AspNetCore.Authentication/DigestAuthenticationUtility.cs deleted file mode 100644 index 097e91de3..000000000 --- a/src/Cuemon.AspNetCore.Authentication/DigestAuthenticationUtility.cs +++ /dev/null @@ -1,205 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text; -using Cuemon.Security.Cryptography; -using Cuemon.Text; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Provides an isolated set of members to work with HTTP Digest access authentication. - /// - public static class DigestAuthenticationUtility - { - internal static readonly byte[] DefaultPrivateKey = Convert.FromBase64String("ZHBGWDRrVGVxbFlhVEpWQ3hoYUc5VUlZM05penNOaUk="); - - /// - /// The value of the header credential user name of a HTTP Digest access authentication. - /// - public const string CredentialUserName = "username"; - - /// - /// The value of the header credential realm of a HTTP Digest access authentication. - /// - public const string CredentialRealm = "realm"; - - /// - /// The value of the header credential response of a HTTP Digest access authentication. - /// - public const string CredentialResponse = "response"; - - /// - /// The value of the header credential quality of protection of a HTTP Digest access authentication. - /// - public const string CredentialQualityOfProtection = "qop"; - - /// - /// The value of the header credential quality of protection options of a HTTP Digest access authentication. - /// - public const string CredentialQualityOfProtectionOptions = "auth,auth-int"; - - /// - /// The value of the header credential client nonce of a HTTP Digest access authentication. - /// - public const string CredentialClientNonce = "cnonce"; - - /// - /// The value of the header credential nonce count of a HTTP Digest access authentication. - /// - public const string CredentialNonceCount = "nc"; - - /// - /// The value of the header credential nonce of a HTTP Digest access authentication. - /// - public const string CredentialNonce = "nonce"; - - /// - /// The value of the header credential digest URI of a HTTP Digest access authentication. - /// - public const string CredentialDigestUri = "uri"; - - /// - /// The value of the header credential opaque of a HTTP Digest access authentication. - /// - public const string CredentialOpaque = "opaque"; - - /// - /// The value of the header credential algorithm of a HTTP Digest access authentication. - /// - public const string CredentialAlgorithm = "algorithm"; - - /// - /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA1. - /// - /// The credentials of the HA1 computed value (, ). - /// The password to include in the HA1 computed value. - /// The algorithm to use when computing the HA1 value. - /// A in the format of H('[CredentialUserName]:[CredentialRealm]:'). - public static string ComputeHash1(IDictionary credentials, string password, UnkeyedCryptoAlgorithm algorithm) - { - ValidateCredentials(credentials, CredentialUserName, CredentialRealm); - return UnkeyedHashFactory.CreateCrypto(algorithm).ComputeHash(string.Format(CultureInfo.InvariantCulture, "{0}:{1}:{2}", credentials[CredentialUserName], credentials[CredentialRealm], password), o => - { - o.Encoding = Encoding.UTF8; - }).ToHexadecimalString(); - } - - /// - /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA2. - /// - /// The credentials of the HA2 computed value (). - /// The HTTP method to include in the HA2 computed value. - /// The algorithm to use when computing the HA2 value. - /// A in the format of H(':[CredentialDigestUri]'). - public static string ComputeHash2(IDictionary credentials, string httpMethod, UnkeyedCryptoAlgorithm algorithm) - { - ValidateCredentials(credentials, CredentialDigestUri); - return UnkeyedHashFactory.CreateCrypto(algorithm).ComputeHash(string.Format(CultureInfo.InvariantCulture, "{0}:{1}", httpMethod, credentials[CredentialDigestUri]), o => - { - o.Encoding = Encoding.UTF8; - }).ToHexadecimalString(); - } - - /// - /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication RESPONSE. - /// - /// The credentials of the RESPONSE computed value (, , , ). - /// The HA1 to include in the RESPONSE computed value. - /// The HA2 to include in the RESPONSE computed value. - /// The algorithm to use when computing the RESPONSE value. - /// A in the format of H(':[CredentialNonce]:[CredentialNonceCount]:[CredentialClientNonce]:[CredentialQualityOfProtection]:'). - public static byte[] ComputeResponse(IDictionary credentials, string hash1, string hash2, UnkeyedCryptoAlgorithm algorithm) - { - ValidateCredentials(credentials, CredentialNonce, CredentialNonceCount, CredentialClientNonce, CredentialQualityOfProtection); - return UnkeyedHashFactory.CreateCrypto(algorithm).ComputeHash(FormattableString.Invariant($"{hash1}:{credentials[CredentialNonce]}:{credentials[CredentialNonceCount]}:{credentials[CredentialClientNonce]}:{credentials[CredentialQualityOfProtection]}:{hash2}"), o => - { - o.Encoding = Encoding.UTF8; - }).GetBytes(); - } - - /// - /// A default implementation of a nonce parser. - /// - /// The nonce protocol. - /// The time-to-live (ttl) of the . - /// true if the specified has expired compared to ; otherwise, false. - public static bool DefaultNonceExpiredParser(string nonce, TimeSpan timeToLive) - { - Validator.ThrowIfNullOrEmpty(nonce, nameof(nonce)); - if (ParserFactory.FromBase64().TryParse(nonce, out var rawNonce)) - { - var nonceProtocol = Convertible.ToString(rawNonce, options => - { - options.Encoding = Encoding.UTF8; - options.Preamble = PreambleSequence.Remove; - }); - var nonceTimestamp = DateTime.ParseExact(nonceProtocol.Substring(0, nonceProtocol.LastIndexOf(':')), "u", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); - var difference = (DateTime.UtcNow - nonceTimestamp); - return (difference > timeToLive); - } - return false; - } - - /// - /// A default implementation of a nonce generator. - /// - /// The value to include in the generated nonce. - /// An opaque identifier to include in the generated nonce. - /// A cryptographic private key to include as a cipher in the generated nonce. - /// A nonce protocol in the format of ':H()'. - public static string DefaultNonceGenerator(DateTime timestamp, string entityTag, byte[] privateKey) - { - Validator.ThrowIfNullOrEmpty(entityTag, nameof(entityTag)); - Validator.ThrowIfNull(privateKey, nameof(privateKey)); - var nonceHash = ComputeNonceHash(timestamp, entityTag, privateKey); - var nonceProtocol = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", timestamp.ToString("u", CultureInfo.InvariantCulture), nonceHash); - return Convert.ToBase64String(Convertible.GetBytes(nonceProtocol, options => - { - options.Encoding = Encoding.UTF8; - options.Preamble = PreambleSequence.Remove; - })); - } - - /// - /// A default implementation of opaque generator. - /// - /// An opaque value consisting of hexadecimal characters with a length of 32 bytes. - public static string DefaultOpaqueGenerator() - { - return Generate.RandomString(32, Alphanumeric.Hexadecimal).ToLowerInvariant(); - } - - /// - /// Converts the specified to its HTTP Digest access authentication header credential algorithm equivalent. - /// - /// The algorithm to convert. - /// A string containing either MD5, SHA-256 or SHA-512-256. - public static string ParseAlgorithm(UnkeyedCryptoAlgorithm algorithm) - { - switch (algorithm) - { - case UnkeyedCryptoAlgorithm.Sha256: - return "SHA-256"; - case UnkeyedCryptoAlgorithm.Sha512: - return "SHA-512-256"; - default: - return "MD5"; - } - } - - private static void ValidateCredentials(IDictionary credentials, params string[] requiredCredentials) - { - Validator.ThrowIfNull(credentials, nameof(credentials)); - foreach (var requiredCredential in requiredCredentials) - { - if (!credentials.ContainsKey(requiredCredential)) { throw new ArgumentException("One or more required credentials are missing.", nameof(credentials)); } - } - } - - private static string ComputeNonceHash(DateTime timeStamp, string entityTag, byte[] privateKey) - { - return UnkeyedHashFactory.CreateCryptoSha256().ComputeHash(timeStamp, entityTag, Convert.ToBase64String(privateKey)).ToHexadecimalString(); - } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestHeaderBuilder.cs b/src/Cuemon.AspNetCore.Authentication/DigestHeaderBuilder.cs new file mode 100644 index 000000000..1b4f5b565 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/DigestHeaderBuilder.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Globalization; +using System.Text; +using Cuemon.Collections.Generic; +using Cuemon.Security.Cryptography; +using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; + +namespace Cuemon.AspNetCore.Authentication +{ + /// + /// Provides a way to fluently represent a HTTP Digest Access Authentication header. + /// + public class DigestHeaderBuilder + { + private readonly IDictionary _dictionary; + + /// + /// Initializes a new instance of the class. + /// + /// The algorithm to use when either computing HA1, HA2 and/or RESPONSE value(s). + /// The dictionary to initialize this instance from. + /// Allowed values for are: , and . + public DigestHeaderBuilder(UnkeyedCryptoAlgorithm algorithm = UnkeyedCryptoAlgorithm.Sha256, IDictionary init = null) + { + Validator.ThrowIfEqual(algorithm, UnkeyedCryptoAlgorithm.Sha1, nameof(algorithm)); + Validator.ThrowIfEqual(algorithm, UnkeyedCryptoAlgorithm.Sha384, nameof(algorithm)); + Algorithm = algorithm; + _dictionary = init == null ? new Dictionary() : new Dictionary(init); + } + + /// + /// Gets the algorithm of the HTTP Digest Access Authentication. + /// + /// The algorithm of the HTTP Digest Access Authentication. + public UnkeyedCryptoAlgorithm Algorithm { get; } + + /// + /// Gets the name of the authentication scheme. + /// + /// The name of the authentication scheme. + public string AuthenticationScheme => "Digest"; + + /// + /// Associates the field with the specified . + /// + /// The username to use in the authentication process. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestHeaderBuilder AddUserName(string username) + { + Validator.ThrowIfNullOrWhitespace(username, nameof(username)); + Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.UserName, username); + return this; + } + + /// + /// Associates the field with the specified . + /// + /// The realm to use in the authentication process. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestHeaderBuilder AddRealm(string realm) + { + Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.Realm, realm); + return this; + } + + /// + /// Associates the field with the specified . + /// + /// The effective request URI to use in the authentication process. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestHeaderBuilder AddUri(string digestUri) + { + Validator.ThrowIfNullOrWhitespace(digestUri, nameof(digestUri)); + Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.DigestUri, digestUri); + return this; + } + + /// + /// Associates the field with the specified . + /// + /// The cryptographic nonce to use in the authentication process. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestHeaderBuilder AddNonce(string nonce) + { + Validator.ThrowIfNullOrWhitespace(nonce, nameof(nonce)); + Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.Nonce, nonce); + return this; + } + + /// + /// Associates the field with the specified . + /// + /// The count of the number of requests to use in the authentication process. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestHeaderBuilder AddNc(int nonceCount) + { + Validator.ThrowIfLowerThan(nonceCount, 0, nameof(nonceCount)); + Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.NonceCount, nonceCount.ToString("x8")); + return this; + } + + /// + /// Associates the field with the specified . + /// + /// The cryptographic client nonce to use in the authentication process. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestHeaderBuilder AddCnonce(string clientNonce = null) + { + if (clientNonce == null) { clientNonce = Generate.RandomString(32); } + Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.ClientNonce, clientNonce); + return this; + } + + /// + /// Associates the field with "auth". + /// + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestHeaderBuilder AddQopAuthentication() + { + Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.QualityOfProtection, "auth"); + return this; + } + + /// + /// Associates the field with "auth-int". + /// + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestHeaderBuilder AddQopAuthenticationIntegrity() + { + Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.QualityOfProtection, "auth-int"); + return this; + } + + /// + /// Associates the field with the specified . + /// + /// The response to use in the authentication process. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestHeaderBuilder AddResponse(string response) + { + Validator.ThrowIfNullOrWhitespace(response, nameof(response)); + Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.Response, response); + return this; + } + + /// + /// Associates any Digest fields found in the HTTP WWW-Authenticate header from the specified . + /// + /// An instance of . + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestHeaderBuilder AddFromWwwAuthenticateHeader(HttpResponse response) + { + Validator.ThrowIfNull(response, nameof(response)); + return AddFromDigestHeader(response.Headers[HeaderNames.WWWAuthenticate], true); + } + + /// + /// Associates any Digest fields found in the HTTP Authorization header from the specified . + /// + /// An instance of . + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestHeaderBuilder AddFromAuthorizationHeader(HttpRequest request) + { + Validator.ThrowIfNull(request, nameof(request)); + return AddFromDigestHeader(request.Headers[HeaderNames.Authorization]); + } + + /// + /// Associates any Digest fields found in the . + /// + /// The header containing Digest fields. + /// if set to true and is part of the , this field is not being added to this instance. + /// DigestHeaderBuilder. + public DigestHeaderBuilder AddFromDigestHeader(string header, bool skipQop = false) + { + Validator.ThrowIfNullOrWhitespace(header, nameof(header)); + Validator.ThrowIfFalse(() => header.StartsWith(AuthenticationScheme), nameof(header), $"Header did not start with {AuthenticationScheme}."); + var headerWithoutScheme = header.Remove(0, AuthenticationScheme.Length + 1); + + var fields = DelimitedString.Split(headerWithoutScheme); + foreach (var field in fields) + { + var kvp = DelimitedString.Split(field, o => o.Delimiter = "="); + var key = kvp[0].Trim(); + var value = kvp[1].Trim('"'); + if (skipQop && key == DigestHeaders.QualityOfProtection) { continue; } + Decorator.Enclose(_dictionary).TryAdd(key, value); + } + return this; + } + + /// + /// Converts this instance to an . + /// + /// An equivalent of this instance. + public ImmutableDictionary ToImmutableDictionary() + { + return _dictionary.ToImmutableDictionary(); + } + + /// + /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA1. + /// + /// The password to include in the HA1 computed value. + /// A in the format of H(::). H is determined by . + public string ComputeHash1(string password) + { + ValidateFields(DigestHeaders.UserName, DigestHeaders.Realm); + return UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(string.Format(CultureInfo.InvariantCulture, "{0}:{1}:{2}", _dictionary[DigestHeaders.UserName], _dictionary[DigestHeaders.Realm], password), o => + { + o.Encoding = Encoding.UTF8; + }).ToHexadecimalString(); + } + + /// + /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA2. + /// + /// The HTTP method to include in the HA2 computed value. + /// The entity body to apply in the signature when qop is set to auth-int. + /// A in the format of H(:) OR H(::H()). H is determined by . + public string ComputeHash2(string method, string entityBody = null) + { + ValidateFields(DigestHeaders.QualityOfProtection, DigestHeaders.DigestUri); + var qop = _dictionary[DigestHeaders.QualityOfProtection]; + var hasIntegrityProtection = qop.Equals("auth-int", StringComparison.OrdinalIgnoreCase); + if (hasIntegrityProtection && entityBody == null) { throw new ArgumentNullException(nameof(entityBody), "The entity body cannot be null when qop is set to auth-int."); } + + var hashFields = !hasIntegrityProtection + ? FormattableString.Invariant($"{method}:{_dictionary[DigestHeaders.DigestUri]}") + : FormattableString.Invariant($"{method}:{_dictionary[DigestHeaders.DigestUri]}:{UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(entityBody, o => o.Encoding = Encoding.UTF8).ToHexadecimalString()}"); + return UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(hashFields, o => + { + o.Encoding = Encoding.UTF8; + }).ToHexadecimalString(); + } + + /// + /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication RESPONSE. + /// + /// The HA1 to include in the RESPONSE computed value. + /// The HA2 to include in the RESPONSE computed value. + /// A in the format of H(:::::). H is determined by . + public string ComputeResponse(string hash1, string hash2) + { + ValidateFields(DigestHeaders.Nonce, DigestHeaders.NonceCount, DigestHeaders.ClientNonce, DigestHeaders.QualityOfProtection); + return UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(FormattableString.Invariant($"{hash1}:{_dictionary[DigestHeaders.Nonce]}:{_dictionary[DigestHeaders.NonceCount]}:{_dictionary[DigestHeaders.ClientNonce]}:{_dictionary[DigestHeaders.QualityOfProtection]}:{hash2}"), o => + { + o.Encoding = Encoding.UTF8; + }).ToHexadecimalString(); + } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + var header = DelimitedString.Create(_dictionary, o => + { + o.Delimiter = ", "; + o.StringConverter = kvp => $"{kvp.Key}=\"{kvp.Value}\""; + }); + return $"{AuthenticationScheme} {header}"; + } + + private void ValidateFields(params string[] requiredFieldNames) + { + foreach (var requiredFieldName in requiredFieldNames) + { + if (!_dictionary.ContainsKey(requiredFieldName)) { throw new ArgumentException("Required field is missing.", requiredFieldName); } + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestHeaders.cs b/src/Cuemon.AspNetCore.Authentication/DigestHeaders.cs new file mode 100644 index 000000000..8b85d7c0d --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/DigestHeaders.cs @@ -0,0 +1,58 @@ +namespace Cuemon.AspNetCore.Authentication +{ + /// + /// Header names for HTTP Digest Access Authentication. + /// + public static class DigestHeaders + { + /// + /// The username field of a HTTP Digest access authentication. + /// + public const string UserName = "username"; + + /// + /// The realm field of a HTTP Digest access authentication. + /// + public const string Realm = "realm"; + + /// + /// The response field of a HTTP Digest access authentication. + /// + public const string Response = "response"; + + /// + /// The qop (quality of protection) field of a HTTP Digest access authentication. + /// + public const string QualityOfProtection = "qop"; + + /// + /// The client nonce (cnonce) field of a HTTP Digest access authentication. + /// + public const string ClientNonce = "cnonce"; + + /// + /// The nc (nonce count) field of a HTTP Digest access authentication. + /// + public const string NonceCount = "nc"; + + /// + /// The nonce field of a HTTP Digest access authentication. + /// + public const string Nonce = "nonce"; + + /// + /// The uri (digest URI) field of a HTTP Digest access authentication. + /// + public const string DigestUri = "uri"; + + /// + /// The opaque field of a HTTP Digest access authentication. + /// + public const string Opaque = "opaque"; + + /// + /// The algorithm field of a HTTP Digest access authentication. + /// + public const string Algorithm = "algorithm"; + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/GlobalSuppressions.cs b/src/Cuemon.AspNetCore.Authentication/GlobalSuppressions.cs new file mode 100644 index 000000000..60f04d4bc --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "Clarity.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Authentication.DigestAccessAuthenticationMiddleware.InvokeAsync(Microsoft.AspNetCore.Http.HttpContext,Cuemon.AspNetCore.Authentication.INonceTracker)~System.Threading.Tasks.Task")] diff --git a/src/Cuemon.AspNetCore.Authentication/INonceTracker.cs b/src/Cuemon.AspNetCore.Authentication/INonceTracker.cs new file mode 100644 index 000000000..d015b936c --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/INonceTracker.cs @@ -0,0 +1,34 @@ +using System; + +namespace Cuemon.AspNetCore.Authentication +{ + /// + /// Represents tracking of server-generated nonce values. + /// + /// + public interface INonceTracker + { + /// + /// Attempts to get the associated with the specified from the tracker. + /// + /// The unique identifier of the tracker. + /// When this method returns, contains the entry associated with the specified , or null if the operation failed. + /// true if the was found in the tracker; otherwise, false. + bool TryGetEntry(string nonce, out NonceTrackerEntry entry); + + /// + /// Attempts to insert a into the tracker. + /// + /// The unique identifier of the tracker. + /// The number or bit string that should be used only once. + /// true if insertion succeeded; otherwise, false when there is already an entry in the tracker with the same key. + bool TryAddEntry(string nonce, int count); + + /// + /// Attempts to remove an entry from the tracker. + /// + /// The unique identifier of the tracker. + /// true if the entry is removed from the tracker; otherwise, false. + bool TryRemoveEntry(string nonce); + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/NonceTracker.cs b/src/Cuemon.AspNetCore.Authentication/NonceTracker.cs new file mode 100644 index 000000000..8e2801d0b --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/NonceTracker.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using Cuemon.Threading; + +namespace Cuemon.AspNetCore.Authentication +{ + /// + /// Provides a default implementation of the interface. + /// + /// + /// + public class NonceTracker : Disposable, INonceTracker + { + private readonly ConcurrentDictionary _entries = new ConcurrentDictionary(); + private readonly Timer _expirationTimer; + + /// + /// Initializes a new instance of the class. + /// + public NonceTracker() + { + _expirationTimer = TimerFactory.CreateNonCapturingTimer(state => ((NonceTracker)state).OnAutomatedSweepCleanup(), this, TimeSpan.FromMinutes(15), TimeSpan.FromHours(1)); + } + + /// + /// Attempts to get the associated with the specified from the tracker. + /// + /// The unique identifier of the tracker. + /// When this method returns, contains the entry associated with the specified , or null if the operation failed. + /// true if the was found in the tracker; otherwise, false. + /// + /// cannot be null. + /// + /// + /// cannot be empty or consist only of white-space characters. + /// + public bool TryGetEntry(string nonce, out NonceTrackerEntry entry) + { + Validator.ThrowIfNullOrWhitespace(nonce, nameof(nonce)); + return _entries.TryGetValue(nonce, out entry); + } + + /// + /// Attempts to insert a into the tracker. + /// + /// The unique identifier of the tracker. + /// The number or bit string that should be used only once. + /// true if insertion succeeded; otherwise, false when there is already an entry in the tracker with the same key. + /// + /// cannot be null. + /// + /// + /// cannot be empty or consist only of white-space characters. + /// + public bool TryAddEntry(string nonce, int count) + { + Validator.ThrowIfNullOrWhitespace(nonce, nameof(nonce)); + return _entries.TryAdd(nonce, new NonceTrackerEntry(count, DateTime.UtcNow)); + } + + /// + /// Attempts to remove an entry from the tracker. + /// + /// The unique identifier of the tracker. + /// true if the entry is removed from the tracker; otherwise, false. + /// + /// cannot be null. + /// + /// + /// cannot be empty or consist only of white-space characters. + /// + public bool TryRemoveEntry(string nonce) + { + Validator.ThrowIfNullOrWhitespace(nonce, nameof(nonce)); + return _entries.TryRemove(nonce, out _); + } + + private void OnAutomatedSweepCleanup() + { + var utcStaleTime = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(5)); + var entries = _entries.Where(pair => pair.Value.Created <= utcStaleTime).ToList(); + if (entries.Count > 0) + { + foreach (var entry in entries) + { + _entries.TryRemove(entry.Key, out _); + } + } + } + + /// + /// Called when this object is being disposed by either or having disposing set to true and is false. + /// + protected override void OnDisposeManagedResources() + { + _expirationTimer?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/NonceTrackerEntry.cs b/src/Cuemon.AspNetCore.Authentication/NonceTrackerEntry.cs new file mode 100644 index 000000000..0b23745bb --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/NonceTrackerEntry.cs @@ -0,0 +1,33 @@ +using System; + +namespace Cuemon.AspNetCore.Authentication +{ + /// + /// Represents an individual nonce entry in the . + /// + public class NonceTrackerEntry + { + /// + /// Initializes a new instance of the class. + /// + /// The number that should be used only once. + /// The timestamp from when this entry was created. + public NonceTrackerEntry(int count, DateTime created) + { + Count = count; + Created = created; + } + + /// + /// Gets the number that should be used only once. + /// + /// The number that should be used only once. + public int Count { get; } + + /// + /// Gets the timestamp from when this entry was created. + /// + /// The timestamp from when this entry was created. + public DateTime Created { get; } + } +} \ No newline at end of file diff --git a/src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..08bb92956 --- /dev/null +++ b/src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs @@ -0,0 +1,23 @@ +using Cuemon.AspNetCore.Authentication; +using Microsoft.Extensions.DependencyInjection; + +namespace Cuemon.Extensions.AspNetCore.Authentication +{ + /// + /// Extension methods for the interface. + /// + public static class ServiceCollectionExtensions + { + /// + /// Adds a service to the specified . + /// + /// The to add services to. + /// An that can be used to further configure other services. + public static IServiceCollection AddDigestAccessAuthenticationNonceTracker(this IServiceCollection services) + { + Validator.ThrowIfNull(services, nameof(services)); + services.AddSingleton(); + return services; + } + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs b/test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs new file mode 100644 index 000000000..96bc2de4d --- /dev/null +++ b/test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs @@ -0,0 +1,306 @@ +using System.IO; +using System.Security.Claims; +using System.Threading.Tasks; +using Cuemon.Collections.Generic; +using Cuemon.Extensions; +using Cuemon.Extensions.AspNetCore.Authentication; +using Cuemon.Extensions.IO; +using Cuemon.Extensions.Xunit; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.IO; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Authentication +{ + public class DigestAccessAuthenticationMiddlewareTest : Test + { + public DigestAccessAuthenticationMiddlewareTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task InvokeAsync_ShouldNotBeAuthenticated() + { + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseFakeHttpResponseTrigger(o => o.ShortCircuitOnStarting = true); + app.UseDigestAccessAuthentication(); + }, services => + { + services.Configure(o => + { + o.Authenticator = (string username, out string password) => + { + if (username == "Agent") + { + password = "Test"; + var cp = new ClaimsPrincipal(); + cp.AddIdentity(new ClaimsIdentity(Arguments.Yield(new Claim("Name", "Test Agent")))); + return cp; + } + password = null; + return null; + }; + o.Realm = "unittest"; + o.RequireSecureConnection = false; + }); + services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); + services.AddDigestAccessAuthenticationNonceTracker(); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + + var wwwAuthenticate = context.Response.Headers[HeaderNames.WWWAuthenticate]; + + TestOutput.WriteLine(wwwAuthenticate); + + var encodedUsernameAndPassword = "Agent:Test".ToByteArray().ToBase64String(); + context.Request.Headers.Add(HeaderNames.Authorization, $"Digest {encodedUsernameAndPassword}"); + + ue = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + } + } + + [Fact] + public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeader() + { + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseFakeHttpResponseTrigger(o => o.ShortCircuitOnStarting = true); + app.UseDigestAccessAuthentication(); + }, services => + { + services.Configure(o => + { + o.Authenticator = (string username, out string password) => + { + if (username == "Agent") + { + password = "Test"; + var cp = new ClaimsPrincipal(); + cp.AddIdentity(new ClaimsIdentity(Arguments.Yield(new Claim("Name", "Test Agent")))); + return cp; + } + password = null; + return null; + }; + o.Realm = "unittest"; + o.RequireSecureConnection = false; + }); + services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); + services.AddDigestAccessAuthenticationNonceTracker(); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + + var wwwAuthenticate = context.Response.Headers[HeaderNames.WWWAuthenticate]; + + + TestOutput.WriteLine(wwwAuthenticate); + + var db = new DigestHeaderBuilder(options.Value.Algorithm) + .AddUserName("Agent") + .AddRealm("unittest") + .AddUri("/") + .AddNc(1) + .AddCnonce() + .AddQopAuthentication() + .AddFromWwwAuthenticateHeader(context.Response); + + + var ha1 = db.ComputeHash1("Test"); + + TestOutput.WriteLine(ha1); + + var ha2 = db.ComputeHash2("GET", context.Response.Body.ToEncodedString()); + var response = db.ComputeResponse(ha1, ha2); + + db.AddResponse(response); + + context.Response.Body = new MemoryStream(); + context.Request.Headers.Add(HeaderNames.Authorization, db.ToString()); + + await pipeline(context); + + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } + } + + [Fact] + public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeaderNoPlainTextPassword() + { + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseFakeHttpResponseTrigger(o => o.ShortCircuitOnStarting = true); + app.UseDigestAccessAuthentication(); + }, services => + { + services.Configure(o => + { + o.Authenticator = (string username, out string password) => + { + if (username == "Agent") + { + password = "a69d6da3eea4fa832dc1c0534863988e550e523f1f786c238951b7ec7abf4d57"; + var cp = new ClaimsPrincipal(); + cp.AddIdentity(new ClaimsIdentity(Arguments.Yield(new Claim("Name", "Test Agent")))); + return cp; + } + password = null; + return null; + }; + o.Realm = "unittest"; + o.RequireSecureConnection = false; + o.DigestAccessSigner = parameters => + { + var db = new DigestHeaderBuilder(parameters.Algorithm, parameters.Credentials); + var ha1 = parameters.Password; // password is ha1 stored in some storage + var ha2 = db.ComputeHash2(parameters.Method, parameters.EntityBody); + return db.ComputeResponse(ha1, ha2); + }; + }); + services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); + services.AddDigestAccessAuthenticationNonceTracker(); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + + var wwwAuthenticate = context.Response.Headers[HeaderNames.WWWAuthenticate]; + + TestOutput.WriteLine(wwwAuthenticate); + + var db = new DigestHeaderBuilder(options.Value.Algorithm) + .AddUserName("Agent") + .AddRealm("unittest") + .AddUri("/") + .AddNc(1) + .AddCnonce() + .AddQopAuthentication() + .AddFromWwwAuthenticateHeader(context.Response); + + + var ha1 = db.ComputeHash1("Test"); + + TestOutput.WriteLine(ha1); + + var ha2 = db.ComputeHash2("GET", context.Response.Body.ToEncodedString()); + var response = db.ComputeResponse(ha1, ha2); + + db.AddResponse(response); + + context.Response.Body = new MemoryStream(); + context.Request.Headers.Add(HeaderNames.Authorization, db.ToString()); + + await pipeline(context); + + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } + } + + [Fact] + public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeaderWithQopIntegrity() + { + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseFakeHttpResponseTrigger(o => o.ShortCircuitOnStarting = true); + app.UseDigestAccessAuthentication(); + }, services => + { + services.Configure(o => + { + o.Authenticator = (string username, out string password) => + { + if (username == "Agent") + { + password = "Test"; + var cp = new ClaimsPrincipal(); + cp.AddIdentity(new ClaimsIdentity(Arguments.Yield(new Claim("Name", "Test Agent")))); + return cp; + } + password = null; + return null; + }; + o.Realm = "unittest"; + o.RequireSecureConnection = false; + }); + services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); + services.AddDigestAccessAuthenticationNonceTracker(); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + + var wwwAuthenticate = context.Response.Headers[HeaderNames.WWWAuthenticate]; + + TestOutput.WriteLine(wwwAuthenticate); + + var db = new DigestHeaderBuilder(options.Value.Algorithm) + .AddUserName("Agent") + .AddRealm("unittest") + .AddUri("/") + .AddNc(1) + .AddCnonce() + .AddQopAuthenticationIntegrity() + .AddFromWwwAuthenticateHeader(context.Response); + + TestOutput.WriteLine("Body:"); + var entityBody = context.Response.Body.ToEncodedString(o => o.LeaveOpen = true); + + var ha1 = db.ComputeHash1("Test"); + + TestOutput.WriteLine("HA1:"); + TestOutput.WriteLine(ha1); + + var ha2 = db.ComputeHash2("GET", context.Response.Body.ToEncodedString()); + var response = db.ComputeResponse(ha1, ha2); + + TestOutput.WriteLine("HA2:"); + TestOutput.WriteLine(ha2); + + db.AddResponse(response); + + context.Response.Body = StreamFactory.Create(writer => writer.Write(entityBody)); + context.Request.Headers.Add(HeaderNames.Authorization, db.ToString()); + + await pipeline(context); + + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } + } + } +} \ No newline at end of file From 3caa7a4b528677950e209e1229b606d46c00424c Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 22 Oct 2020 00:56:19 +0200 Subject: [PATCH 350/385] Added a more interactive way of doing exception handling (using Func as predicate). --- src/Cuemon.Core/Validator.cs | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/Cuemon.Core/Validator.cs b/src/Cuemon.Core/Validator.cs index 3e879d66a..5d1f58555 100644 --- a/src/Cuemon.Core/Validator.cs +++ b/src/Cuemon.Core/Validator.cs @@ -235,6 +235,48 @@ public static void ThrowIfFalse(bool value, string paramName, string message = " } } + /// + /// Validates and throws an if the specified returns true. + /// + /// The function delegate that determines if an is thrown. + /// The name of the parameter that caused the exception. + /// A message that describes the error. + /// + /// returned true. + /// + public static void ThrowIfTrue(Func predicate, string paramName, string message) + { + try + { + ThrowWhen(c => c.IsTrue(predicate).Create(() => new ArgumentException(message, paramName)).TryThrow()); + } + catch (ArgumentException ex) + { + throw ExceptionInsights.Embed(ex, MethodBase.GetCurrentMethod(), Arguments.ToArray(predicate, paramName, message)); + } + } + + /// + /// Validates and throws an if the specified returns false. + /// + /// The function delegate that determines if an is thrown. + /// The name of the parameter that caused the exception. + /// A message that describes the error. + /// + /// returned false. + /// + public static void ThrowIfFalse(Func predicate, string paramName, string message) + { + try + { + ThrowWhen(c => c.IsFalse(predicate).Create(() => new ArgumentException(message, paramName)).TryThrow()); + } + catch (ArgumentException ex) + { + throw ExceptionInsights.Embed(ex, MethodBase.GetCurrentMethod(), Arguments.ToArray(predicate, paramName, message)); + } + } + /// /// Validates and throws an if the specified has no elements. /// From b00bbc5733c089be5549c66a719d5a91bc55f7fe Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 22 Oct 2020 00:56:55 +0200 Subject: [PATCH 351/385] Tweaks and additions. --- .../Http/FakeHttpContextAccessor.cs | 8 ++++---- .../Http/Features/FakeHttpRequestFeature.cs | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs index 85562bcf8..c0b9a427b 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs @@ -1,5 +1,5 @@ -using System.IO; -using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features; +using Cuemon.IO; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; @@ -19,9 +19,9 @@ public FakeHttpContextAccessor() { var fc = new FeatureCollection(); fc.Set(new FakeHttpResponseFeature()); - fc.Set(new HttpRequestFeature()); + fc.Set(new FakeHttpRequestFeature()); HttpContext = new DefaultHttpContext(fc); - HttpContext.Response.Body = new MemoryStream(); + HttpContext.Response.Body = StreamFactory.Create(writer => writer.WriteLine("Hello awesome developers!")); } /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs new file mode 100644 index 000000000..9ccd7d733 --- /dev/null +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs @@ -0,0 +1,19 @@ +using System.Net.Http; +using Microsoft.AspNetCore.Http.Features; + +namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features +{ + public class FakeHttpRequestFeature : HttpRequestFeature + { + /// + /// Initializes a new instance of the class. + /// + public FakeHttpRequestFeature() + { + Method = HttpMethod.Get.ToString(); + Path = "/"; + Scheme = "http"; + Protocol = "HTTP/1.1"; + } + } +} \ No newline at end of file From 69a3a097ee60e0b5533eb452fe23b3cfeb06766f Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 22 Oct 2020 01:07:25 +0200 Subject: [PATCH 352/385] Wording. --- .../Http/Features/FakeHttpRequestFeature.cs | 4 ++++ .../Http/Features/FakeHttpResponseFeature.cs | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs index 9ccd7d733..fba4953f8 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs @@ -3,6 +3,10 @@ namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features { + /// + /// Represents a way to support some default values for Request context.. + /// + /// public class FakeHttpRequestFeature : HttpRequestFeature { /// diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs index 48fc0fcea..01ad3590e 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs @@ -5,9 +5,9 @@ namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features { /// - /// Represents a way to trigger . + /// Represents a way to trigger . /// - /// + /// public class FakeHttpResponseFeature : HttpResponseFeature { private bool _hasStarted; From e141313b7af3d00cba4dff0b27add5b4e653d9bc Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 22 Oct 2020 01:09:09 +0200 Subject: [PATCH 353/385] Fixed a bug so Split will process quoted strings correctly (at least until a new bug is discovered) :-) --- src/Cuemon.Core/DelimitedString.cs | 7 +-- test/Cuemon.Core.Tests/DelimitedStringTest.cs | 59 +++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 test/Cuemon.Core.Tests/DelimitedStringTest.cs diff --git a/src/Cuemon.Core/DelimitedString.cs b/src/Cuemon.Core/DelimitedString.cs index 934f2c48b..658854431 100644 --- a/src/Cuemon.Core/DelimitedString.cs +++ b/src/Cuemon.Core/DelimitedString.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; -using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -46,7 +45,7 @@ public static string Create(IEnumerable source, ActionThe which may be configured. /// A that contain the substrings of delimited by a and optionally surrounded within . /// - /// This method was inspired by two articles on StackOverflow @ http://stackoverflow.com/questions/2807536/split-string-in-c-sharp and https://stackoverflow.com/questions/3776458/split-a-comma-separated-string-with-both-quoted-and-unquoted-strings. + /// This method was inspired by two articles on StackOverflow @ http://stackoverflow.com/questions/2807536/split-string-in-c-sharp, https://stackoverflow.com/questions/3776458/split-a-comma-separated-string-with-both-quoted-and-unquoted-strings and https://stackoverflow.com/questions/6542996/how-to-split-csv-whose-columns-may-contain. /// The default implementation conforms with the RFC-4180 standard. /// /// @@ -62,13 +61,13 @@ public static string[] Split(string value, Action setup var key = string.Concat(delimiter, "<-dq->", qualifier); if (!CompiledSplitExpressions.TryGetValue(key, out var compiledSplit)) { - compiledSplit = new Regex(string.Format(CultureInfo.InvariantCulture, "(?:^|{0})({1}(?:[^{1}]+|{1}{1})*{1}|[^{0}]*)", Regex.Escape(delimiter), Regex.Escape(qualifier)), RegexOptions.IgnoreCase | RegexOptions.Compiled, TimeSpan.FromSeconds(2)); + compiledSplit = new Regex(string.Format(CultureInfo.InvariantCulture, "{0}(?=(?:[^{1}]*{1}[^{1}]*{1})*(?![^{1}]*{1}))", delimiter, qualifier), RegexOptions.IgnoreCase | RegexOptions.Compiled, TimeSpan.FromSeconds(2)); CompiledSplitExpressions.TryAdd(key, compiledSplit); } try { - return compiledSplit.Matches(value).Cast().Where(m => m.Length > 0).Select(m => m.Value.TrimStart(delimiter.ToCharArray())).ToArray(); + return compiledSplit.Split(value); } catch (RegexMatchTimeoutException) { diff --git a/test/Cuemon.Core.Tests/DelimitedStringTest.cs b/test/Cuemon.Core.Tests/DelimitedStringTest.cs new file mode 100644 index 000000000..0e88f4d06 --- /dev/null +++ b/test/Cuemon.Core.Tests/DelimitedStringTest.cs @@ -0,0 +1,59 @@ +using Cuemon.Extensions.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon +{ + public class DelimitedStringTest : Test + { + public DelimitedStringTest(ITestOutputHelper output) : base(output) + { + } + + + [Fact] + public void Split_ShouldSplitPreservingQualifierOfTypeDoubleQuote() + { + var s1 = "1999,Chevy,\"Venture \"\"Extended Edition, Very Large\"\"\",,5000.00"; + var s2 = "realm=\"unittest\", qop=\"auth, auth-int\", nonce=\"MjAyMC0xMC0yMCAyMzoxMzo0MFo6OWY1NmRjZTY0NWI3YjY5YjhlM2NlOTFhNDM2ZWI2ZGFiNDIxYzY5MjU4YzI1YTBkNDg1M2RkYTQ2NmRkOWJkNg==\""; + + var ds1 = DelimitedString.Split(s1); + var ds2 = DelimitedString.Split(s2); + + TestOutput.WriteLine("---- ds1 -----"); + + TestOutput.WriteLine(s1); + + TestOutput.WriteLine("---- ds2 -----"); + + TestOutput.WriteLine(s2); + + TestOutput.WriteLine("---- foreach sc in ds1 -----"); + + foreach (var sc in ds1) + { + TestOutput.WriteLine(sc); + } + + TestOutput.WriteLine("---- foreach sc in ds2 -----"); + + foreach (var sc in ds2) + { + TestOutput.WriteLine(sc); + } + + TestOutput.WriteLine(new string('-', 5)); + + var j1 = DelimitedString.Create(ds1); + var j2 = DelimitedString.Create(ds2); + + TestOutput.WriteLine(j1); + + Assert.Equal(s1, j1); + Assert.Equal(s2, j2); + + Assert.True(ds1.Length == 5); + Assert.True(ds2.Length == 3); + } + } +} From fc91d1903f9bb80931b09b94a96487a466c5d2db Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 22 Oct 2020 01:32:44 +0200 Subject: [PATCH 354/385] Fixed unit test. --- .../Http/FakeHttpContextAccessor.cs | 2 +- .../AspNetCoreHostTestTest.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs index c0b9a427b..4d995281f 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs @@ -21,7 +21,7 @@ public FakeHttpContextAccessor() fc.Set(new FakeHttpResponseFeature()); fc.Set(new FakeHttpRequestFeature()); HttpContext = new DefaultHttpContext(fc); - HttpContext.Response.Body = StreamFactory.Create(writer => writer.WriteLine("Hello awesome developers!")); + HttpContext.Response.Body = StreamFactory.Create(writer => writer.Write("Hello awesome developers!")); } /// diff --git a/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/AspNetCoreHostTestTest.cs b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/AspNetCoreHostTestTest.cs index 3a8cd2c90..059e3bd1b 100644 --- a/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/AspNetCoreHostTestTest.cs +++ b/test/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Tests/AspNetCoreHostTestTest.cs @@ -30,7 +30,7 @@ public async Task ShouldHaveResultOfBoolMiddlewareInBody() var options = _provider.GetRequiredService>(); var pipeline = _pipeline.Build(); - Assert.Equal("", context.Response.Body.ToEncodedString(o => o.LeaveOpen = true)); + Assert.Equal("Hello awesome developers!", context.Response.Body.ToEncodedString(o => o.LeaveOpen = true)); await pipeline(context); From 73fc4bf38c47faf22b60bcf1eaf1d06fac6b3301 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Thu, 22 Oct 2020 22:42:26 +0200 Subject: [PATCH 355/385] Increased wait time due to random error in test. --- test/Cuemon.Core.Tests/DisposableTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Cuemon.Core.Tests/DisposableTest.cs b/test/Cuemon.Core.Tests/DisposableTest.cs index b060fd2de..c15335821 100644 --- a/test/Cuemon.Core.Tests/DisposableTest.cs +++ b/test/Cuemon.Core.Tests/DisposableTest.cs @@ -172,7 +172,7 @@ public void UnmanagedDisposable_VerifyThatAssetIsBeingDisposedOnFinalize() GC.WaitForPendingFinalizers(); } - Thread.Sleep(1500); + Thread.Sleep(3500); // await GC if (unmanaged.TryGetTarget(out var ud2)) { From 97c2fc2ca577f3a5e410693f92a51b59d563cf00 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 22 Feb 2021 03:43:42 +0100 Subject: [PATCH 356/385] Base classes and refactoring. --- ...henticationUtility.cs => Authenticator.cs} | 4 +- .../AuthorizationHeader.cs | 69 +++++ .../AuthorizationHeaderBuilder.cs | 127 ++++++++ .../AuthorizationHeaderOptions.cs | 46 +++ .../BasicAuthenticationMiddleware.cs | 93 ------ .../BasicAuthenticationOptions.cs | 28 -- .../BasicAuthenticator.cs | 12 - .../Cuemon.AspNetCore.Authentication.csproj | 1 + .../DigestAccessAuthenticationMiddleware.cs | 138 --------- .../DigestAccessAuthenticationOptions.cs | 151 ---------- .../DigestAccessAuthenticationParameters.cs | 58 ---- .../DigestAccessAuthenticator.cs | 12 - .../DigestHeaderBuilder.cs | 277 ------------------ .../DigestHeaders.cs | 58 ---- .../HmacAuthenticationMiddleware.cs | 97 ------ .../HmacAuthenticationOptions.cs | 61 ---- .../HmacAuthenticationParameters.cs | 41 --- .../HmacAuthenticator.cs | 12 - 18 files changed, 245 insertions(+), 1040 deletions(-) rename src/Cuemon.AspNetCore.Authentication/{AuthenticationUtility.cs => Authenticator.cs} (97%) create mode 100644 src/Cuemon.AspNetCore.Authentication/AuthorizationHeader.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/AuthorizationHeaderBuilder.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/AuthorizationHeaderOptions.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/BasicAuthenticationOptions.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/BasicAuthenticator.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticator.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/DigestHeaderBuilder.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/DigestHeaders.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/HmacAuthenticationOptions.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/HmacAuthenticationParameters.cs delete mode 100644 src/Cuemon.AspNetCore.Authentication/HmacAuthenticator.cs diff --git a/src/Cuemon.AspNetCore.Authentication/AuthenticationUtility.cs b/src/Cuemon.AspNetCore.Authentication/Authenticator.cs similarity index 97% rename from src/Cuemon.AspNetCore.Authentication/AuthenticationUtility.cs rename to src/Cuemon.AspNetCore.Authentication/Authenticator.cs index 931f4e2a0..f76dda15f 100644 --- a/src/Cuemon.AspNetCore.Authentication/AuthenticationUtility.cs +++ b/src/Cuemon.AspNetCore.Authentication/Authenticator.cs @@ -7,9 +7,9 @@ namespace Cuemon.AspNetCore.Authentication { /// - /// Provides a set of generic ways to work with HTTP based authentication. + /// Provides a set of static methods for working with HTTP based authentication. /// - public static class AuthenticationUtility + public static class Authenticator { /// /// Provides a generic way to make authentication requests using the specified . diff --git a/src/Cuemon.AspNetCore.Authentication/AuthorizationHeader.cs b/src/Cuemon.AspNetCore.Authentication/AuthorizationHeader.cs new file mode 100644 index 000000000..d49fd894a --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/AuthorizationHeader.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Cuemon.Collections.Generic; + +namespace Cuemon.AspNetCore.Authentication +{ + /// + /// Represents the base class from which all implementations of authorization header should derive. + /// + public abstract class AuthorizationHeader + { + /// + /// Initializes a new instance of the class. + /// + /// The name of the authentication scheme. + protected AuthorizationHeader(string authenticationScheme) + { + Validator.ThrowIfNullOrWhitespace(authenticationScheme, nameof(authenticationScheme)); + AuthenticationScheme = authenticationScheme; + } + + /// + /// Gets the name of the authentication scheme. + /// + /// The name of the authentication scheme. + public string AuthenticationScheme { get; } + + /// + /// Parses the specified . + /// + /// The authorization header to parse. + /// The which need to be configured. + /// An equivalent of . + public virtual AuthorizationHeader Parse(string authorizationHeader, Action setup) + { + Validator.ThrowIfNullOrWhitespace(authorizationHeader, nameof(authorizationHeader)); + Validator.ThrowIfFalse(() => authorizationHeader.StartsWith(AuthenticationScheme), nameof(authorizationHeader), $"Header did not start with {AuthenticationScheme}."); + + var options = Patterns.Configure(setup); + var headerWithoutScheme = authorizationHeader.Remove(0, AuthenticationScheme.Length + 1); + var credentials = DelimitedString.Split(headerWithoutScheme, o => o.Delimiter = options.CredentialsDelimiter).ToList(); + + var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var credential in credentials) + { + var kvp = DelimitedString.Split(credential, o => o.Delimiter = options.CredentialsKeyValueDelimiter); + var key = kvp[0].Trim(); + var value = kvp[1].Trim('"'); + Decorator.Enclose(dictionary).TryAdd(key, value); + } + + return ParseCore(dictionary); + } + + /// + /// The core parser that resolves an from a set of . + /// + /// The credentials used in authentication. + /// An equivalent of . + protected abstract AuthorizationHeader ParseCore(IReadOnlyDictionary credentials); + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public abstract override string ToString(); + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/AuthorizationHeaderBuilder.cs b/src/Cuemon.AspNetCore.Authentication/AuthorizationHeaderBuilder.cs new file mode 100644 index 000000000..7d249ff14 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/AuthorizationHeaderBuilder.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Cuemon.Collections.Generic; + +namespace Cuemon.AspNetCore.Authentication +{ + /// + /// Represents the base class from which all implementations of authorization header builders should derive. + /// Implements the + /// + /// The type of the authorization header result. + /// The type of the authorization header builder. + public abstract class AuthorizationHeaderBuilder : AuthorizationHeaderBuilder + where TAuthorizationHeader : AuthorizationHeader + where TAuthorizationHeaderBuilder : AuthorizationHeaderBuilder + { + /// + /// Initializes a new instance of the class. + /// + /// The name of the authentication scheme. + protected AuthorizationHeaderBuilder(string authenticationScheme) : base(authenticationScheme) + { + } + + /// + /// Attempts to add or update an existing field with the provided with the specified . + /// + /// The name of the field to add or update. + /// The value of the field to add or update. + /// that can be used to further build the header. + public TAuthorizationHeaderBuilder AddOrUpdate(string name, string value) + { + Validator.ThrowIfNullOrWhitespace(name, nameof(name)); + Decorator.Enclose(Data).AddOrUpdate(name, value); + return this as TAuthorizationHeaderBuilder; + } + + /// + /// Builds an instance of that implements . + /// + /// . + public abstract TAuthorizationHeader Build(); + } + + /// + /// The base class of an . + /// + public abstract class AuthorizationHeaderBuilder + { + /// + /// Initializes a new instance of the class. + /// + /// The name of the authentication scheme. + protected AuthorizationHeaderBuilder(string authenticationScheme) + { + Validator.ThrowIfNullOrWhitespace(authenticationScheme, nameof(authenticationScheme)); + AuthenticationScheme = authenticationScheme; + } + + /// + /// Gets the fields added to this instance. + /// + /// The fields added to this instance. + protected IDictionary Data { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Gets the relations added to this instance. + /// + /// The relations added to this instance. + protected IDictionary Relation { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Gets the name of the authentication scheme. + /// + /// The name of the authentication scheme. + public string AuthenticationScheme { get; } + + /// + /// Maps the logical relation between a and the associated. + /// + /// The name of a member. + /// The field names to associate with . + protected void MapRelation(string memberName, params string[] fieldNames) + { + Validator.ThrowIfNullOrWhitespace(memberName, nameof(memberName)); + Validator.ThrowIfSequenceNullOrEmpty(fieldNames, nameof(fieldNames)); + foreach (var key in fieldNames) { Decorator.Enclose(Relation).AddOrUpdate(key, memberName); } + } + + /// + /// Validates that any has been added to . + /// + /// The required field names to validate. + /// + /// The required field is missing. + /// + protected void ValidateData(params string[] requiredFieldNames) + { + foreach (var rfn in requiredFieldNames) + { + var invalidState = !Data.ContainsKey(rfn) || Data[rfn] == null; + if (Relation.TryGetValue(rfn, out var member) && invalidState) { throw new ArgumentException($"Required field is missing. Did you forget to invoke {member}?", rfn); } + if (invalidState) { throw new ArgumentException("Required field is missing.", rfn); } + } + } + + /// + /// Converts this instance to an . + /// + /// An equivalent of this instance. + public ImmutableDictionary ToImmutableDictionary() + { + return Data.ToImmutableDictionary(); + } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + return DelimitedString.Create(Data.Keys.Select(key => $"{key}={Data[key]}")); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/AuthorizationHeaderOptions.cs b/src/Cuemon.AspNetCore.Authentication/AuthorizationHeaderOptions.cs new file mode 100644 index 000000000..7b8216109 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/AuthorizationHeaderOptions.cs @@ -0,0 +1,46 @@ +namespace Cuemon.AspNetCore.Authentication +{ + /// + /// Configuration options for . + /// + public class AuthorizationHeaderOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// , + /// + /// + /// + /// = + /// + /// + /// + public AuthorizationHeaderOptions() + { + CredentialsDelimiter = ","; + CredentialsKeyValueDelimiter = "="; + } + + /// + /// Gets or sets the credentials delimiter. + /// + /// The credentials delimiter. + public string CredentialsDelimiter { get; set; } + + /// + /// Gets or sets the credentials key value delimiter. + /// + /// The credentials key value delimiter. + public string CredentialsKeyValueDelimiter { get; set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs deleted file mode 100644 index 08781f885..000000000 --- a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationMiddleware.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Security.Claims; -using System.Text; -using System.Threading.Tasks; -using Cuemon.IO; -using Cuemon.Text; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Options; -using Microsoft.Net.Http.Headers; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Provides a HTTP Basic Authentication middleware implementation for ASP.NET Core. - /// - public class BasicAuthenticationMiddleware : ConfigurableMiddleware - { - /// - /// Initializes a new instance of the class. - /// - /// The delegate of the request pipeline to invoke. - /// The which need to be configured. - public BasicAuthenticationMiddleware(RequestDelegate next, IOptions setup) : base(next, setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The delegate of the request pipeline to invoke. - /// The middleware which need to be configured. - public BasicAuthenticationMiddleware(RequestDelegate next, Action setup) : base(next, setup) - { - } - - /// - /// Executes the . - /// - /// The context of the current request. - /// A task that represents the execution of this middleware. - public override async Task InvokeAsync(HttpContext context) - { - if (!AuthenticationUtility.TryAuthenticate(context, Options.RequireSecureConnection, AuthorizationHeaderParser, TryAuthenticate)) - { - await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (message, response) => - { - context.Response.OnStarting(() => - { - context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{AuthenticationScheme} realm=\"{Options.Realm}\"")); - return Task.CompletedTask; - }); - response.StatusCode = (int)message.StatusCode; - await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); - }).ConfigureAwait(false); - } - await Next(context).ConfigureAwait(false); - } - - /// - /// Gets the name of the authentication scheme. - /// - /// The name of the authentication scheme. - public string AuthenticationScheme => "Basic"; - - private bool TryAuthenticate(HttpContext context, Template credentials, out ClaimsPrincipal result) - { - if (Options.Authenticator == null) { throw new InvalidOperationException(FormattableString.Invariant($"The {nameof(Options.Authenticator)} cannot be null.")); } - result = Options.Authenticator(credentials.Arg1, credentials.Arg2); - return Condition.IsNotNull(result); - } - - private Template AuthorizationHeaderParser(HttpContext context, string authorizationHeader) - { - if (AuthenticationUtility.IsAuthenticationSchemeValid(authorizationHeader, AuthenticationScheme)) - { - var base64Credentials = authorizationHeader.Remove(0, AuthenticationScheme.Length + 1); - if (Condition.IsBase64(base64Credentials)) - { - var credentials = Convertible.ToString(Convert.FromBase64String(base64Credentials), options => - { - options.Encoding = Encoding.ASCII; - options.Preamble = PreambleSequence.Remove; - }).Split(':'); - if (credentials.Length == 2 && - !string.IsNullOrEmpty(credentials[0]) && - !string.IsNullOrEmpty(credentials[1])) - { return Template.CreateTwo(credentials[0], credentials[1]); } - } - } - return null; - } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationOptions.cs deleted file mode 100644 index 26599646f..000000000 --- a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticationOptions.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Configuration options for . This class cannot be inherited. - /// - /// - public sealed class BasicAuthenticationOptions : AuthenticationOptions - { - /// - /// Initializes a new instance of the class. - /// - public BasicAuthenticationOptions() - { - } - - /// - /// Gets or sets the function delegate that will perform the authentication from the specified username and password. - /// - /// The function delegate that will perform the authentication. - public BasicAuthenticator Authenticator { get; set; } - - /// - /// Gets the realm that defines the protection space. - /// - /// The realm that defines the protection space. - public string Realm { get; set; } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticator.cs b/src/Cuemon.AspNetCore.Authentication/BasicAuthenticator.cs deleted file mode 100644 index 2d92cd552..000000000 --- a/src/Cuemon.AspNetCore.Authentication/BasicAuthenticator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Security.Claims; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Represents the method that defines an Authenticator typically assigned on . - /// - /// The username that must be paired with . - /// The password that must be paired with . - /// A that is associated with the result of and . - public delegate ClaimsPrincipal BasicAuthenticator(string username, string password); -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj index 6f72201cf..1a1f9965d 100644 --- a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj +++ b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs deleted file mode 100644 index 23957b727..000000000 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationMiddleware.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Collections.Immutable; -using System.Security.Claims; -using System.Threading.Tasks; -using Cuemon.IO; -using Cuemon.Security.Cryptography; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Options; -using Microsoft.Net.Http.Headers; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Provides a HTTP Digest Access Authentication middleware implementation for ASP.NET Core. - /// - public class DigestAccessAuthenticationMiddleware : ConfigurableMiddleware - { - private INonceTracker _nonceTracker; - - /// - /// Initializes a new instance of the class. - /// - /// The delegate of the request pipeline to invoke. - /// The which need to be configured. - public DigestAccessAuthenticationMiddleware(RequestDelegate next, IOptions setup) : base(next, setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The delegate of the request pipeline to invoke. - /// The middleware which need to be configured. - public DigestAccessAuthenticationMiddleware(RequestDelegate next, Action setup) : base(next, setup) - { - } - - /// - /// Executes the . - /// - /// The context of the current request. - /// The dependency injected implementation of an . - /// A task that represents the execution of this middleware. - public override async Task InvokeAsync(HttpContext context, INonceTracker nonceTracker) - { - _nonceTracker = nonceTracker; - if (!AuthenticationUtility.TryAuthenticate(context, Options.RequireSecureConnection, AuthorizationHeaderParser, TryAuthenticate)) - { - await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (message, response) => - { - context.Response.OnStarting(() => - { - string etag = context.Response.Headers[HeaderNames.ETag]; - if (string.IsNullOrEmpty(etag)) { etag = "no-entity-tag"; } - var opaqueGenerator = Options.OpaqueGenerator; - var nonceSecret = Options.NonceSecret; - var nonceGenerator = Options.NonceGenerator; - var staleNonce = context.Items["staleNonce"] as string ?? "false"; - context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{AuthenticationScheme} realm=\"{Options.Realm}\", qop=\"auth, auth-int\", nonce=\"{nonceGenerator(DateTime.UtcNow, etag, nonceSecret())}\", opaque=\"{opaqueGenerator()}\", stale=\"{staleNonce}\", algorithm=\"{ParseAlgorithm(Options.Algorithm)}\"")); - return Task.CompletedTask; - }); - response.StatusCode = (int)message.StatusCode; - await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); - }).ConfigureAwait(false); - } - await Next.Invoke(context).ConfigureAwait(false); - } - - /// - /// Gets the name of the authentication scheme. - /// - /// The name of the authentication scheme. - public string AuthenticationScheme => "Digest"; - - private bool TryAuthenticate(HttpContext context, ImmutableDictionary credentials, out ClaimsPrincipal result) - { - if (Options.Authenticator == null) { throw new InvalidOperationException(FormattableString.Invariant($"The {nameof(Options.Authenticator)} delegate cannot be null.")); } - credentials.TryGetValue(DigestHeaders.UserName, out var userName); - credentials.TryGetValue(DigestHeaders.Response, out var clientResponse); - credentials.TryGetValue(DigestHeaders.NonceCount, out var nonceCount); - if (credentials.TryGetValue(DigestHeaders.Nonce, out var nonce)) - { - result = null; - var nonceExpiredParser = Options.NonceExpiredParser; - var staleNonce = nonceExpiredParser(nonce, TimeSpan.FromSeconds(30)); - context.Items["staleNonce"] = staleNonce.ToString().ToUpperInvariant(); - if (staleNonce) { return false; } - - if (_nonceTracker != null) - { - var nc = Convert.ToInt32(nonceCount, 16); - if (_nonceTracker.TryGetEntry(nonce, out var previousNonce)) - { - if (previousNonce.Count == nc) { return false; } - } - else - { - _nonceTracker.TryAddEntry(nonce, nc); - } - } - } - result = Options.Authenticator(userName, out var password); - var serverResponse = Options?.DigestAccessSigner(new DigestAccessAuthenticationParameters(credentials, context.Request.Method, password, Decorator.Enclose(context.Response.Body).ToEncodedString(o => o.LeaveOpen = true), Options.Algorithm)); - return serverResponse != null && serverResponse.Equals(clientResponse, StringComparison.Ordinal) && Condition.IsNotNull(result); - } - - internal ImmutableDictionary AuthorizationHeaderParser(HttpContext context, string authorizationHeader) - { - var id = new DigestHeaderBuilder(Options.Algorithm) - .AddFromDigestHeader(authorizationHeader) - .ToImmutableDictionary(); - return IsDigestCredentialsValid(id) ? id : null; - } - - private static bool IsDigestCredentialsValid(ImmutableDictionary credentials) - { - var valid = credentials.ContainsKey("username"); - valid |= credentials.ContainsKey("realm"); - valid |= credentials.ContainsKey("nonce"); - valid |= credentials.ContainsKey("uri"); - valid |= credentials.ContainsKey("response"); - return valid; - } - - private static string ParseAlgorithm(UnkeyedCryptoAlgorithm algorithm) - { - switch (algorithm) - { - case UnkeyedCryptoAlgorithm.Sha256: - return "SHA-256"; - case UnkeyedCryptoAlgorithm.Sha512: - return "SHA-512-256"; - default: - return "MD5"; - } - } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs deleted file mode 100644 index c079c5e42..000000000 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationOptions.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System; -using System.Globalization; -using System.Text; -using Cuemon.Security.Cryptography; -using Cuemon.Text; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Configuration options for . This class cannot be inherited. - /// - /// - public sealed class DigestAccessAuthenticationOptions : AuthenticationOptions - { - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// - /// - /// - /// - /// null - /// - /// - /// - /// A default implementation of a nonce generator. - /// - /// - /// - /// A default implementation of an opaque generator. - /// - /// - /// - /// A default implementation of a nonce expiry parser. - /// - /// - /// - /// A default secret to get you started without overwhelming configuration. Do change when moving outside a development environment. - /// - /// - /// - /// A default implementation of HTTP Digest Access RESPONSE. - /// - /// - /// - /// AuthenticationServer - /// - /// - /// - public DigestAccessAuthenticationOptions() - { - Algorithm = UnkeyedCryptoAlgorithm.Sha256; - OpaqueGenerator = () => Generate.RandomString(32, Alphanumeric.Hexadecimal).ToLowerInvariant(); - NonceExpiredParser = (nonce, timeToLive) => - { - Validator.ThrowIfNullOrEmpty(nonce, nameof(nonce)); - if (ParserFactory.FromBase64().TryParse(nonce, out var rawNonce)) - { - var nonceProtocol = Convertible.ToString(rawNonce, options => - { - options.Encoding = Encoding.UTF8; - options.Preamble = PreambleSequence.Remove; - }); - var nonceTimestamp = DateTime.ParseExact(nonceProtocol.Substring(0, nonceProtocol.LastIndexOf(':')), "u", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); - var difference = (DateTime.UtcNow - nonceTimestamp); - return (difference > timeToLive); - } - return false; - }; - NonceGenerator = (timestamp, entityTag, privateKey) => - { - Validator.ThrowIfNullOrWhitespace(entityTag, nameof(entityTag)); - Validator.ThrowIfNull(privateKey, nameof(privateKey)); - var nonceHash = UnkeyedHashFactory.CreateCryptoSha256().ComputeHash(timestamp.Ticks, entityTag, Convert.ToBase64String(privateKey)).ToHexadecimalString(); - var nonceProtocol = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", timestamp.ToString("u", CultureInfo.InvariantCulture), nonceHash); - return Convert.ToBase64String(Convertible.GetBytes(nonceProtocol, options => - { - options.Encoding = Encoding.UTF8; - options.Preamble = PreambleSequence.Remove; - })); - }; - NonceSecret = () => Convert.FromBase64String("ZHBGWDRrVGVxbFlhVEpWQ3hoYUc5VUlZM05penNOaUk="); - DigestAccessSigner = parameters => - { - var db = new DigestHeaderBuilder(parameters.Algorithm, parameters.Credentials); - var ha1 = db.ComputeHash1(parameters.Password); - var ha2 = db.ComputeHash2(parameters.Method, parameters.EntityBody); - return db.ComputeResponse(ha1, ha2); - }; - Realm = "AuthenticationServer"; - } - - /// - /// Gets or sets the function delegate that will perform the authentication from the specified username. - /// - /// The function delegate that will perform the authentication. - public DigestAccessAuthenticator Authenticator { get; set; } - - /// - /// Gets or sets the function delegate that will sign a message retrieved from a HTTP request. - /// - /// The function delegate that will sign a message. - public Func DigestAccessSigner { get; set; } - - /// - /// Gets or sets the algorithm of the HTTP Digest Access Authentication. Default is . - /// - /// The algorithm of the HTTP Digest Access Authentication. - /// Allowed values are: , and . - public UnkeyedCryptoAlgorithm Algorithm { get; set; } - - /// - /// Gets the realm that defines the protection space. - /// - /// The realm that defines the protection space. - public string Realm { get; set; } - - /// - /// Gets or sets the function delegate for generating opaque string values. - /// - /// The function delegate for generating opaque string values. - public Func OpaqueGenerator { get; set; } - - /// - /// Gets or sets the function delegate for retrieving the cryptographic secret used in nonce string values. - /// - /// The function delegate for retrieving the cryptographic secret used in nonce string values. - public Func NonceSecret { get; set; } - - /// - /// Gets or sets the function delegate for generating nonce string values. - /// - /// The function delegate for generating nonce string values. - public Func NonceGenerator { get; set; } - - /// - /// Gets or sets the function delegate for parsing nonce string values for expiration. - /// - /// The function delegate for parsing nonce string values for expiration. - public Func NonceExpiredParser { get; set; } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs deleted file mode 100644 index 425f05a16..000000000 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticationParameters.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Immutable; -using Cuemon.Security.Cryptography; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Represents a set of parameters that is needed for creating an application of cryptographic hashing with usage of nonce values to prevent replay attacks. - /// - public class DigestAccessAuthenticationParameters - { - /// - /// Initializes a new instance of the class. - /// - /// The credentials used in the computation of HA1-, HA2-, and response hash values. - /// The HTTP method to include in the HA2 computed value. - /// The password to include in the HA1 computed value. - /// The entity body to apply in the signature when qop is set to auth-int. - /// The algorithm to use when computing the HA1-, HA2-, and response hash values. - internal DigestAccessAuthenticationParameters(ImmutableDictionary credentials, string method, string password, string entityBody, UnkeyedCryptoAlgorithm algorithm) - { - Credentials = credentials; - Method = method; - Password = password; - EntityBody = entityBody; - Algorithm = algorithm; - } - - /// - /// Gets the credentials used in the computation of HA1-, HA2-, and RESPONSE hash values. - /// - /// The credentials used in the computation of HA1-, HA2-, and RESPONSE hash values. - public ImmutableDictionary Credentials { get; } - - /// - /// Gets the HTTP method to include in the HA2 computed value. - /// - /// The HTTP method to include in the HA2 computed value. - public string Method { get; } - - /// - /// Gets the password to include in the HA1 computed value. - /// - /// The password to include in the HA1 computed value. - public string Password { get; } - - /// - /// Gets the algorithm to use when computing the HA1-, HA2-, and response hash values. - /// - /// The algorithm to use when computing the HA1-, HA2-, and response hash values. - public UnkeyedCryptoAlgorithm Algorithm { get; } - - /// - /// Gets the entity body to include in the HA2 computed value. - /// - /// The entity body to include in the HA2 computed value. - public string EntityBody { get; } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticator.cs b/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticator.cs deleted file mode 100644 index f87945b2d..000000000 --- a/src/Cuemon.AspNetCore.Authentication/DigestAccessAuthenticator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Security.Claims; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Represents the method that defines an Authenticator typically assigned on . - /// - /// The username to match and lookup the paired . - /// The password paired with . - /// A that is associated with the result of . - public delegate ClaimsPrincipal DigestAccessAuthenticator(string username, out string password); -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestHeaderBuilder.cs b/src/Cuemon.AspNetCore.Authentication/DigestHeaderBuilder.cs deleted file mode 100644 index 1b4f5b565..000000000 --- a/src/Cuemon.AspNetCore.Authentication/DigestHeaderBuilder.cs +++ /dev/null @@ -1,277 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Text; -using Cuemon.Collections.Generic; -using Cuemon.Security.Cryptography; -using Microsoft.AspNetCore.Http; -using Microsoft.Net.Http.Headers; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Provides a way to fluently represent a HTTP Digest Access Authentication header. - /// - public class DigestHeaderBuilder - { - private readonly IDictionary _dictionary; - - /// - /// Initializes a new instance of the class. - /// - /// The algorithm to use when either computing HA1, HA2 and/or RESPONSE value(s). - /// The dictionary to initialize this instance from. - /// Allowed values for are: , and . - public DigestHeaderBuilder(UnkeyedCryptoAlgorithm algorithm = UnkeyedCryptoAlgorithm.Sha256, IDictionary init = null) - { - Validator.ThrowIfEqual(algorithm, UnkeyedCryptoAlgorithm.Sha1, nameof(algorithm)); - Validator.ThrowIfEqual(algorithm, UnkeyedCryptoAlgorithm.Sha384, nameof(algorithm)); - Algorithm = algorithm; - _dictionary = init == null ? new Dictionary() : new Dictionary(init); - } - - /// - /// Gets the algorithm of the HTTP Digest Access Authentication. - /// - /// The algorithm of the HTTP Digest Access Authentication. - public UnkeyedCryptoAlgorithm Algorithm { get; } - - /// - /// Gets the name of the authentication scheme. - /// - /// The name of the authentication scheme. - public string AuthenticationScheme => "Digest"; - - /// - /// Associates the field with the specified . - /// - /// The username to use in the authentication process. - /// An that can be used to further build the HTTP Digest Access Authentication header. - public DigestHeaderBuilder AddUserName(string username) - { - Validator.ThrowIfNullOrWhitespace(username, nameof(username)); - Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.UserName, username); - return this; - } - - /// - /// Associates the field with the specified . - /// - /// The realm to use in the authentication process. - /// An that can be used to further build the HTTP Digest Access Authentication header. - public DigestHeaderBuilder AddRealm(string realm) - { - Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.Realm, realm); - return this; - } - - /// - /// Associates the field with the specified . - /// - /// The effective request URI to use in the authentication process. - /// An that can be used to further build the HTTP Digest Access Authentication header. - public DigestHeaderBuilder AddUri(string digestUri) - { - Validator.ThrowIfNullOrWhitespace(digestUri, nameof(digestUri)); - Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.DigestUri, digestUri); - return this; - } - - /// - /// Associates the field with the specified . - /// - /// The cryptographic nonce to use in the authentication process. - /// An that can be used to further build the HTTP Digest Access Authentication header. - public DigestHeaderBuilder AddNonce(string nonce) - { - Validator.ThrowIfNullOrWhitespace(nonce, nameof(nonce)); - Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.Nonce, nonce); - return this; - } - - /// - /// Associates the field with the specified . - /// - /// The count of the number of requests to use in the authentication process. - /// An that can be used to further build the HTTP Digest Access Authentication header. - public DigestHeaderBuilder AddNc(int nonceCount) - { - Validator.ThrowIfLowerThan(nonceCount, 0, nameof(nonceCount)); - Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.NonceCount, nonceCount.ToString("x8")); - return this; - } - - /// - /// Associates the field with the specified . - /// - /// The cryptographic client nonce to use in the authentication process. - /// An that can be used to further build the HTTP Digest Access Authentication header. - public DigestHeaderBuilder AddCnonce(string clientNonce = null) - { - if (clientNonce == null) { clientNonce = Generate.RandomString(32); } - Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.ClientNonce, clientNonce); - return this; - } - - /// - /// Associates the field with "auth". - /// - /// An that can be used to further build the HTTP Digest Access Authentication header. - public DigestHeaderBuilder AddQopAuthentication() - { - Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.QualityOfProtection, "auth"); - return this; - } - - /// - /// Associates the field with "auth-int". - /// - /// An that can be used to further build the HTTP Digest Access Authentication header. - public DigestHeaderBuilder AddQopAuthenticationIntegrity() - { - Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.QualityOfProtection, "auth-int"); - return this; - } - - /// - /// Associates the field with the specified . - /// - /// The response to use in the authentication process. - /// An that can be used to further build the HTTP Digest Access Authentication header. - public DigestHeaderBuilder AddResponse(string response) - { - Validator.ThrowIfNullOrWhitespace(response, nameof(response)); - Decorator.Enclose(_dictionary).TryAdd(DigestHeaders.Response, response); - return this; - } - - /// - /// Associates any Digest fields found in the HTTP WWW-Authenticate header from the specified . - /// - /// An instance of . - /// An that can be used to further build the HTTP Digest Access Authentication header. - public DigestHeaderBuilder AddFromWwwAuthenticateHeader(HttpResponse response) - { - Validator.ThrowIfNull(response, nameof(response)); - return AddFromDigestHeader(response.Headers[HeaderNames.WWWAuthenticate], true); - } - - /// - /// Associates any Digest fields found in the HTTP Authorization header from the specified . - /// - /// An instance of . - /// An that can be used to further build the HTTP Digest Access Authentication header. - public DigestHeaderBuilder AddFromAuthorizationHeader(HttpRequest request) - { - Validator.ThrowIfNull(request, nameof(request)); - return AddFromDigestHeader(request.Headers[HeaderNames.Authorization]); - } - - /// - /// Associates any Digest fields found in the . - /// - /// The header containing Digest fields. - /// if set to true and is part of the , this field is not being added to this instance. - /// DigestHeaderBuilder. - public DigestHeaderBuilder AddFromDigestHeader(string header, bool skipQop = false) - { - Validator.ThrowIfNullOrWhitespace(header, nameof(header)); - Validator.ThrowIfFalse(() => header.StartsWith(AuthenticationScheme), nameof(header), $"Header did not start with {AuthenticationScheme}."); - var headerWithoutScheme = header.Remove(0, AuthenticationScheme.Length + 1); - - var fields = DelimitedString.Split(headerWithoutScheme); - foreach (var field in fields) - { - var kvp = DelimitedString.Split(field, o => o.Delimiter = "="); - var key = kvp[0].Trim(); - var value = kvp[1].Trim('"'); - if (skipQop && key == DigestHeaders.QualityOfProtection) { continue; } - Decorator.Enclose(_dictionary).TryAdd(key, value); - } - return this; - } - - /// - /// Converts this instance to an . - /// - /// An equivalent of this instance. - public ImmutableDictionary ToImmutableDictionary() - { - return _dictionary.ToImmutableDictionary(); - } - - /// - /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA1. - /// - /// The password to include in the HA1 computed value. - /// A in the format of H(::). H is determined by . - public string ComputeHash1(string password) - { - ValidateFields(DigestHeaders.UserName, DigestHeaders.Realm); - return UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(string.Format(CultureInfo.InvariantCulture, "{0}:{1}:{2}", _dictionary[DigestHeaders.UserName], _dictionary[DigestHeaders.Realm], password), o => - { - o.Encoding = Encoding.UTF8; - }).ToHexadecimalString(); - } - - /// - /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA2. - /// - /// The HTTP method to include in the HA2 computed value. - /// The entity body to apply in the signature when qop is set to auth-int. - /// A in the format of H(:) OR H(::H()). H is determined by . - public string ComputeHash2(string method, string entityBody = null) - { - ValidateFields(DigestHeaders.QualityOfProtection, DigestHeaders.DigestUri); - var qop = _dictionary[DigestHeaders.QualityOfProtection]; - var hasIntegrityProtection = qop.Equals("auth-int", StringComparison.OrdinalIgnoreCase); - if (hasIntegrityProtection && entityBody == null) { throw new ArgumentNullException(nameof(entityBody), "The entity body cannot be null when qop is set to auth-int."); } - - var hashFields = !hasIntegrityProtection - ? FormattableString.Invariant($"{method}:{_dictionary[DigestHeaders.DigestUri]}") - : FormattableString.Invariant($"{method}:{_dictionary[DigestHeaders.DigestUri]}:{UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(entityBody, o => o.Encoding = Encoding.UTF8).ToHexadecimalString()}"); - return UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(hashFields, o => - { - o.Encoding = Encoding.UTF8; - }).ToHexadecimalString(); - } - - /// - /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication RESPONSE. - /// - /// The HA1 to include in the RESPONSE computed value. - /// The HA2 to include in the RESPONSE computed value. - /// A in the format of H(:::::). H is determined by . - public string ComputeResponse(string hash1, string hash2) - { - ValidateFields(DigestHeaders.Nonce, DigestHeaders.NonceCount, DigestHeaders.ClientNonce, DigestHeaders.QualityOfProtection); - return UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(FormattableString.Invariant($"{hash1}:{_dictionary[DigestHeaders.Nonce]}:{_dictionary[DigestHeaders.NonceCount]}:{_dictionary[DigestHeaders.ClientNonce]}:{_dictionary[DigestHeaders.QualityOfProtection]}:{hash2}"), o => - { - o.Encoding = Encoding.UTF8; - }).ToHexadecimalString(); - } - - /// - /// Returns a that represents this instance. - /// - /// A that represents this instance. - public override string ToString() - { - var header = DelimitedString.Create(_dictionary, o => - { - o.Delimiter = ", "; - o.StringConverter = kvp => $"{kvp.Key}=\"{kvp.Value}\""; - }); - return $"{AuthenticationScheme} {header}"; - } - - private void ValidateFields(params string[] requiredFieldNames) - { - foreach (var requiredFieldName in requiredFieldNames) - { - if (!_dictionary.ContainsKey(requiredFieldName)) { throw new ArgumentException("Required field is missing.", requiredFieldName); } - } - } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/DigestHeaders.cs b/src/Cuemon.AspNetCore.Authentication/DigestHeaders.cs deleted file mode 100644 index 8b85d7c0d..000000000 --- a/src/Cuemon.AspNetCore.Authentication/DigestHeaders.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Header names for HTTP Digest Access Authentication. - /// - public static class DigestHeaders - { - /// - /// The username field of a HTTP Digest access authentication. - /// - public const string UserName = "username"; - - /// - /// The realm field of a HTTP Digest access authentication. - /// - public const string Realm = "realm"; - - /// - /// The response field of a HTTP Digest access authentication. - /// - public const string Response = "response"; - - /// - /// The qop (quality of protection) field of a HTTP Digest access authentication. - /// - public const string QualityOfProtection = "qop"; - - /// - /// The client nonce (cnonce) field of a HTTP Digest access authentication. - /// - public const string ClientNonce = "cnonce"; - - /// - /// The nc (nonce count) field of a HTTP Digest access authentication. - /// - public const string NonceCount = "nc"; - - /// - /// The nonce field of a HTTP Digest access authentication. - /// - public const string Nonce = "nonce"; - - /// - /// The uri (digest URI) field of a HTTP Digest access authentication. - /// - public const string DigestUri = "uri"; - - /// - /// The opaque field of a HTTP Digest access authentication. - /// - public const string Opaque = "opaque"; - - /// - /// The algorithm field of a HTTP Digest access authentication. - /// - public const string Algorithm = "algorithm"; - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs deleted file mode 100644 index de363a6b6..000000000 --- a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationMiddleware.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using Cuemon.IO; -using Cuemon.Security.Cryptography; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Options; -using Microsoft.Net.Http.Headers; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Provides a HTTP HMAC Authentication middleware implementation for ASP.NET Core. - /// - public class HmacAuthenticationMiddleware : ConfigurableMiddleware - { - /// - /// Initializes a new instance of the class. - /// - /// The delegate of the request pipeline to invoke. - /// The which need to be configured. - public HmacAuthenticationMiddleware(RequestDelegate next, IOptions setup) : base(next, setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The delegate of the request pipeline to invoke. - /// The middleware which need to be configured. - public HmacAuthenticationMiddleware(RequestDelegate next, Action setup) : base(next, setup) - { - } - - /// - /// Executes the . - /// - /// The context of the current request. - /// A task that represents the execution of this middleware. - public override async Task InvokeAsync(HttpContext context) - { - if (!AuthenticationUtility.TryAuthenticate(context, Options.RequireSecureConnection, AuthorizationHeaderParser, TryAuthenticate)) - { - await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (message, response) => - { - context.Response.OnStarting(() => - { - context.Response.Headers.Add(HeaderNames.WWWAuthenticate, Options.AuthenticationScheme); - return Task.CompletedTask; - }); - response.StatusCode = (int)message.StatusCode; - await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); - }).ConfigureAwait(false); - } - await Next.Invoke(context).ConfigureAwait(false); - } - - private bool TryAuthenticate(HttpContext context, Template credentials, out ClaimsPrincipal result) - { - if (Options.Authenticator == null) { throw new InvalidOperationException(FormattableString.Invariant($"The {nameof(Options.Authenticator)} cannot be null.")); } - var requestBodyMd5 = context.Request.Headers[HeaderNames.ContentMD5].FirstOrDefault()?.ToLowerInvariant(); - if (!string.IsNullOrWhiteSpace(requestBodyMd5) && !UnkeyedHashFactory.CreateCrypto(UnkeyedCryptoAlgorithm.Md5).ComputeHash(context.Request.Body).ToHexadecimalString().Equals(requestBodyMd5, StringComparison.Ordinal)) - { - result = null; - return false; - } - var publicKey = credentials.Arg1; - var signature = credentials.Arg2; - var stringToSign = Options.MessageDescriptor(context); - var privateKey = new byte[0]; - result = Options?.Authenticator(publicKey, out privateKey); - if (privateKey == null) - { - result = null; - return false; - } - var computedSignature = Options?.HmacSigner(new HmacAuthenticationParameters(Options.Algorithm, privateKey, stringToSign)); - return computedSignature != null && signature.Equals(Convert.ToBase64String(computedSignature), StringComparison.Ordinal) && Condition.IsNotNull(result); - } - - private Template AuthorizationHeaderParser(HttpContext context, string authorizationHeader) - { - if (AuthenticationUtility.IsAuthenticationSchemeValid(authorizationHeader, Options.AuthenticationScheme) && authorizationHeader.Length > Options.AuthenticationScheme.Length) - { - var credentials = authorizationHeader.Remove(0, Options.AuthenticationScheme.Length + 1).Split(':'); - if (credentials.Length == 2) - { - var publicKey = credentials[0]; - var signature = credentials[1]; - if (!string.IsNullOrWhiteSpace(publicKey) && !string.IsNullOrWhiteSpace(signature)) { return Template.CreateTwo(publicKey, signature); } - } - } - return null; - } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationOptions.cs deleted file mode 100644 index 21caa9038..000000000 --- a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationOptions.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using Cuemon.Security.Cryptography; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.Net.Http.Headers; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Configuration options for . This class cannot be inherited. - /// - /// - public sealed class HmacAuthenticationOptions : AuthenticationOptions - { - /// - /// Initializes a new instance of the class. - /// - public HmacAuthenticationOptions() - { - AuthenticationScheme = "HMAC"; - Algorithm = KeyedCryptoAlgorithm.HmacSha1; - MessageDescriptor = context => FormattableString.Invariant($"{context.Request.Method}:{context.Request.GetDisplayUrl()}:{context.Request.Headers[HeaderNames.ContentMD5].FirstOrDefault()}:{context.Request.Headers[HeaderNames.ContentType].FirstOrDefault()}:{context.Request.Headers[HeaderNames.Date].FirstOrDefault()}:{context.Request.Headers[HeaderNames.UserAgent].FirstOrDefault()}"); - HmacSigner = parameters => KeyedHashFactory.CreateHmacCrypto(parameters.PrivateKey, parameters.Algorithm).ComputeHash(parameters.Message, o => - { - o.Encoding = Encoding.UTF8; - }).GetBytes(); - } - - /// - /// Gets the name of the authentication scheme. Default is "HMAC". - /// - /// The name of the authentication scheme. - public string AuthenticationScheme { get; set; } - - /// - /// Gets or sets the algorithm of the HMAC Authentication. Default is . - /// - /// The algorithm of the HMAC Authentication. - public KeyedCryptoAlgorithm Algorithm { get; set; } - - /// - /// Gets or sets the function delegate that provides information about the message to be signed. - /// - /// The function delegate that provides information about the message to be signed. - public Func MessageDescriptor { get; set; } - - /// - /// Gets or sets the function delegate that will perform the authentication from the specified publicKey. - /// - /// The function delegate that will perform the authentication. - public HmacAuthenticator Authenticator { get; set; } - - /// - /// Gets or sets the function delegate that will sign a message retrieved by . - /// - /// The function delegate that will sign a message. - public Func HmacSigner { get; set; } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationParameters.cs b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationParameters.cs deleted file mode 100644 index 3bfb37914..000000000 --- a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticationParameters.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Cuemon.Security.Cryptography; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Represents a set of parameters that is needed for creating a keyed-hash message authentication code (HMAC). - /// - public class HmacAuthenticationParameters - { - /// - /// Initializes a new instance of the class. - /// - /// The hash algorithm to use for the computation. - /// The secret key for the hashed encryption. The key can be any length, but it is strongly recommended to use a size of either 64 bytes (for and ) or 128 bytes (for and ). - /// The value to compute a hash code for. - internal HmacAuthenticationParameters(KeyedCryptoAlgorithm algorithm, byte[] privateKey, string message) - { - Algorithm = algorithm; - Message = message; - PrivateKey = privateKey; - } - - /// - /// Gets the algorithm of the HMAC. Default is . - /// - /// The algorithm of the HMAC. - public KeyedCryptoAlgorithm Algorithm { get; } - - /// - /// Gets the secret key for the hashed encryption. - /// - /// The secret key for the hashed encryption. - public byte[] PrivateKey { get; } - - /// - /// Gets the message to compute a hash code for. - /// - /// The message to compute a hash code for. - public string Message { get; } - } -} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticator.cs b/src/Cuemon.AspNetCore.Authentication/HmacAuthenticator.cs deleted file mode 100644 index c85196dec..000000000 --- a/src/Cuemon.AspNetCore.Authentication/HmacAuthenticator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Security.Claims; - -namespace Cuemon.AspNetCore.Authentication -{ - /// - /// Represents the method that defines an Authenticator typically assigned on . - /// - /// The public key to match and lookup the paired shared secret-. - /// The shared secret-private key paired with . - /// A that is associated with the result of . - public delegate ClaimsPrincipal HmacAuthenticator(string publicKey, out byte[] privateKey); -} \ No newline at end of file From 316a993218f5433ea5c444e4126f7cbd0698c1c6 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 22 Feb 2021 04:21:23 +0100 Subject: [PATCH 357/385] Refactored to use new base classes. --- .../Hmac/HmacAuthenticationMiddleware.cs | 101 ++++++++++ .../Hmac/HmacAuthenticationOptions.cs | 38 ++++ .../Hmac/HmacAuthenticator.cs | 12 ++ .../Hmac/HmacAuthorizationHeader.cs | 136 +++++++++++++ .../Hmac/HmacAuthorizationHeaderBuilder.cs | 181 ++++++++++++++++++ .../Hmac/HmacFields.cs | 58 ++++++ 6 files changed, 526 insertions(+) create mode 100644 src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticationMiddleware.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticationOptions.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticator.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeader.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeaderBuilder.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Hmac/HmacFields.cs diff --git a/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticationMiddleware.cs new file mode 100644 index 000000000..8f687d15d --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticationMiddleware.cs @@ -0,0 +1,101 @@ +using System; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Cuemon.IO; +using Cuemon.Security.Cryptography; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; + +namespace Cuemon.AspNetCore.Authentication.Hmac +{ + /// + /// Provides a HTTP HMAC Authentication middleware implementation for ASP.NET Core. + /// + public class HmacAuthenticationMiddleware : ConfigurableMiddleware + { + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + /// The which need to be configured. + public HmacAuthenticationMiddleware(RequestDelegate next, IOptions setup) : base(next, setup) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + /// The middleware which need to be configured. + public HmacAuthenticationMiddleware(RequestDelegate next, Action setup) : base(next, setup) + { + } + + /// + /// Executes the . + /// + /// The context of the current request. + /// A task that represents the execution of this middleware. + public override async Task InvokeAsync(HttpContext context) + { + if (!Authenticator.TryAuthenticate(context, Options.RequireSecureConnection, AuthorizationHeaderParser, TryAuthenticate)) + { + await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (message, response) => + { + context.Response.OnStarting(() => + { + context.Response.Headers.Add(HeaderNames.WWWAuthenticate, Options.AuthenticationScheme); + return Task.CompletedTask; + }); + response.StatusCode = (int)message.StatusCode; + await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); + }).ConfigureAwait(false); + } + await Next.Invoke(context).ConfigureAwait(false); + } + + private bool TryAuthenticate(HttpContext context, HmacAuthorizationHeader header, out ClaimsPrincipal result) + { + if (Options.Authenticator == null) { throw new InvalidOperationException(FormattableString.Invariant($"The {nameof(Options.Authenticator)} cannot be null.")); } + + if (header == null) + { + result = null; + return false; + } + + var requestBodyMd5 = context.Request.Headers[HeaderNames.ContentMD5].FirstOrDefault()?.ToLowerInvariant(); + if (!string.IsNullOrWhiteSpace(requestBodyMd5) && !UnkeyedHashFactory.CreateCrypto(UnkeyedCryptoAlgorithm.Md5).ComputeHash(context.Request.Body).ToHexadecimalString().Equals(requestBodyMd5, StringComparison.Ordinal)) + { + result = null; + return false; + } + + var clientId = header.ClientId; + result = Options.Authenticator(clientId, out var clientSecret); + if (clientSecret == null) + { + result = null; + return false; + } + var signature = header.Signature; + + var hb = new HmacAuthorizationHeaderBuilder(Options.AuthenticationScheme) + .AddCredentialScope(header.CredentialScope) + .AddClientId(clientId) + .AddClientSecret(clientSecret) + .AddSignedHeaders(header.SignedHeaders) + .AddFromRequest(context.Request); + + var computedSignature = hb.ComputeSignature(); + return computedSignature != null && signature.Equals(computedSignature, StringComparison.Ordinal) && Condition.IsNotNull(result); + } + + private HmacAuthorizationHeader AuthorizationHeaderParser(HttpContext context, string authorizationHeader) + { + return HmacAuthorizationHeader.Create(Options.AuthenticationScheme, authorizationHeader); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticationOptions.cs new file mode 100644 index 000000000..2bb6ad860 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticationOptions.cs @@ -0,0 +1,38 @@ +using Cuemon.Security.Cryptography; + +namespace Cuemon.AspNetCore.Authentication.Hmac +{ + /// + /// Configuration options for . This class cannot be inherited. + /// + /// + public sealed class HmacAuthenticationOptions : AuthenticationOptions + { + /// + /// Initializes a new instance of the class. + /// + public HmacAuthenticationOptions() + { + AuthenticationScheme = HmacAuthorizationHeader.Scheme; + Algorithm = KeyedCryptoAlgorithm.HmacSha1; + } + + /// + /// Gets the name of the authentication scheme. Default is . + /// + /// The name of the authentication scheme. + public string AuthenticationScheme { get; set; } + + /// + /// Gets or sets the algorithm of the HMAC Authentication. Default is . + /// + /// The algorithm of the HMAC Authentication. + public KeyedCryptoAlgorithm Algorithm { get; set; } + + /// + /// Gets or sets the function delegate that will perform the authentication from the specified publicKey. + /// + /// The function delegate that will perform the authentication. + public HmacAuthenticator Authenticator { get; set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticator.cs b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticator.cs new file mode 100644 index 000000000..24d49516a --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthenticator.cs @@ -0,0 +1,12 @@ +using System.Security.Claims; + +namespace Cuemon.AspNetCore.Authentication.Hmac +{ + /// + /// Represents the method that defines an Authenticator typically assigned on . + /// + /// The public key to match and lookup the paired shared . + /// The shared secret-private key paired with . + /// A that is associated with the result of . + public delegate ClaimsPrincipal HmacAuthenticator(string clientId, out string clientSecret); +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeader.cs b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeader.cs new file mode 100644 index 000000000..92ef66728 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeader.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Cuemon.AspNetCore.Authentication.Hmac +{ + /// + /// Provides a representation of a HTTP HMAC Authentication header. + /// Implements the + /// + /// + public class HmacAuthorizationHeader : AuthorizationHeader + { + /// + /// Creates an instance of from the specified parameters. + /// + /// The name of the authentication scheme. + /// The raw HTTP authorization header. + /// The which may be configured. + /// An instance of . + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// cannot be empty or consist only of white-space characters -or- + /// cannot be empty or consist only of white-space characters. + /// + public static HmacAuthorizationHeader Create(string authenticationScheme, string authorizationHeader, Action setup = null) + { + Validator.ThrowIfNullOrWhitespace(authenticationScheme, nameof(authenticationScheme)); + Validator.ThrowIfNullOrWhitespace(authorizationHeader, nameof(authorizationHeader)); + return new HmacAuthorizationHeader(authenticationScheme).Parse(authorizationHeader, setup) as HmacAuthorizationHeader; + } + + /// + /// The default authentication scheme of the . + /// + public const string Scheme = "HMAC"; + + HmacAuthorizationHeader(string authenticationScheme) : base(authenticationScheme) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client identifier that is the public key of the signing process. + /// The credential scope that defines the remote resource. + /// The headers that will be part of the signing process. + /// The signature that represents the integrity of this header. + /// The authentication scheme of this header. Default is (HMAC). + public HmacAuthorizationHeader(string clientId, string credentialScope, string signedHeaders, string signature, string authenticationScheme = Scheme) : base(authenticationScheme) + { + ClientId = clientId; + CredentialScope = credentialScope; + SignedHeaders = signedHeaders.Split(';'); + Signature = signature; + } + + /// + /// Gets the client identifier that is the public key of the signing process. + /// + /// The client identifier that is the public key of the signing process. + public string ClientId { get; } + + /// + /// Gets the credential scope that defines the remote resource. + /// + /// The credential scope that defines the remote resource. + public string CredentialScope { get; } + + /// + /// Gets the headers that will be part of the signing process. + /// + /// The headers that will be part of the signing process. + public string[] SignedHeaders { get; } + + /// + /// Gets the signature that represents the integrity of this header. + /// + /// The signature that represents the integrity of this header. + public string Signature { get; } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + return $"{AuthenticationScheme} Credential={ClientId}/{CredentialScope}, SignedHeaders={string.Join(";", SignedHeaders)}, Signature={Signature}"; + } + + /// + /// The core parser that resolves an from a set of . + /// + /// The credentials used in authentication. + /// An equivalent of . + protected override AuthorizationHeader ParseCore(IReadOnlyDictionary credentials) + { + string clientId = null, credentialScope = null, signedHeaders = null, signature = null; + foreach (var kvp in credentials) + { + var key = kvp.Key; + var value = kvp.Value; + + if (key == "Credential") + { + var cs = value.Split(new [] { '/' }, 2); + clientId = cs[0]; + credentialScope = cs[1]; + } + + if (key == "SignedHeaders") + { + signedHeaders = value; + } + + if (key == "Signature") + { + signature = value; + } + } + + if (!string.IsNullOrWhiteSpace(clientId) && + credentialScope != null && + signedHeaders != null && signedHeaders.Any() && + !string.IsNullOrWhiteSpace(signature)) + { + return new HmacAuthorizationHeader(clientId, credentialScope, signedHeaders, signature); + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeaderBuilder.cs b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeaderBuilder.cs new file mode 100644 index 000000000..8d4e01e28 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeaderBuilder.cs @@ -0,0 +1,181 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Text; +using Cuemon.AspNetCore.Authentication.Digest; +using Cuemon.Collections.Generic; +using Cuemon.Net; +using Cuemon.Security.Cryptography; +using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; + +namespace Cuemon.AspNetCore.Authentication.Hmac +{ + /// + /// Provides a way to fluently represent a HTTP HMAC Authentication header. + /// Inspired by AWS Signature Version 4 (https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html, https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html). + /// + public class HmacAuthorizationHeaderBuilder : AuthorizationHeaderBuilder + { + /// + /// Initializes a new instance of the class. + /// + /// The name of the authentication scheme. Default is HMAC. + /// The algorithm to use when computing the final signature of the HMAC Authentication header. + /// The algorithm to use when computing in-between signatures as part of the final signing of the HMAC Authentication header. + public HmacAuthorizationHeaderBuilder(string authenticationScheme = HmacAuthorizationHeader.Scheme, KeyedCryptoAlgorithm hmacAlgorithm = KeyedCryptoAlgorithm.HmacSha256, UnkeyedCryptoAlgorithm algorithm = UnkeyedCryptoAlgorithm.Sha256) : base(authenticationScheme) + { + HmacAlgorithm = hmacAlgorithm; + Algorithm = algorithm; + MapRelation(nameof(AddCredentialScope), HmacFields.CredentialScope); + MapRelation(nameof(AddClientId), HmacFields.ClientId); + MapRelation(nameof(AddClientSecret), HmacFields.ClientSecret); + MapRelation(nameof(AddFromRequest), HmacFields.UriPath, HmacFields.UriQuery, HmacFields.HttpHeaders, HmacFields.Payload, HmacFields.ServerDateTime); + } + + /// + /// Gets the non-keyed algorithm of the HTTP HMAC Authentication. + /// + /// The non-keyed algorithm of the HTTP HMAC Authentication. + public UnkeyedCryptoAlgorithm Algorithm { get; } + + /// + /// Gets the keyed algorithm of the HTTP HMAC Authentication. + /// + /// The keyed algorithm of the HTTP HMAC Authentication. + public KeyedCryptoAlgorithm HmacAlgorithm { get; } + + + /// + /// Adds the credential scope that defines the remote resource. + /// + /// The credential scope that defines the remote resource. + /// A reference to this instance after the operation has completed. + /// An that can be used to further build the HTTP HMAC Authentication header. + public HmacAuthorizationHeaderBuilder AddCredentialScope(string credentialScope) + { + return AddOrUpdate(HmacFields.CredentialScope, credentialScope); + } + + /// + /// Adds the client identifier that is the public key of the signing process. + /// + /// The client identifier that is the public key of the signing process. + /// An that can be used to further build the HTTP HMAC Authentication header. + public HmacAuthorizationHeaderBuilder AddClientId(string clientId) + { + return AddOrUpdate(HmacFields.ClientId, clientId); + } + + /// + /// Adds the client secret that is the private key of the signing process. + /// + /// The client secret that is the private key of the signing process. + /// An that can be used to further build the HTTP HMAC Authentication header. + public HmacAuthorizationHeaderBuilder AddClientSecret(string clientSecret) + { + return AddOrUpdate(HmacFields.ClientSecret, clientSecret); + } + + /// + /// Adds the necessary fields that is part of an HTTP request. + /// + /// An instance of the object. + /// An that can be used to further build the HTTP HMAC Authentication header. + public HmacAuthorizationHeaderBuilder AddFromRequest(HttpRequest request) + { + Validator.ThrowIfNull(request, nameof(request)); + return AddOrUpdate(HmacFields.HttpMethod, request.Method) + .AddOrUpdate(HmacFields.UriPath, request.Path.ToUriComponent()) + .AddOrUpdate(HmacFields.UriQuery, string.Concat(request.Query.OrderBy(pair => pair.Key).Select(pair => $"{Decorator.Enclose(pair.Value.ToString()).UrlEncode()}"))) + .AddOrUpdate(HmacFields.HttpHeaders, request.Headers.Count == 0 ? null : string.Concat(request.Headers.OrderBy(pair => pair.Key).Select(pair => $"{pair.Key.ToLowerInvariant()}:{DelimitedString.Create(pair.Value, o => o.StringConverter = s => $"{s.Trim()}{Alphanumeric.Linefeed}")}"))) + .AddOrUpdate(HmacFields.Payload, UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(request.Body).ToHexadecimalString()) + .AddOrUpdate(HmacFields.ServerDateTime, DateTime.Parse(request.Headers[HeaderNames.Date].ToString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind).ToString("O")); + } + + /// + /// Adds the headers that will be part of the signing process. + /// + /// The headers that will be part of the signing process. Default is host and date + /// An that can be used to further build the HTTP HMAC Authentication header. + public HmacAuthorizationHeaderBuilder AddSignedHeaders(params string[] signedHeaders) + { + if (signedHeaders == null) { return this; } + return AddOrUpdate(HmacFields.SignedHeaders, DelimitedString.Create(signedHeaders, o => + { + o.Delimiter = ";"; + o.StringConverter = s => s.ToLowerInvariant(); + })); + } + + /// + /// Converts the request to a standardized (canonical) format and computes a message digest using . + /// + /// A representation, in hexadecimal, of the computed canonical request. + public virtual string ComputeCanonicalRequest() + { + ValidateData(HmacFields.UriPath, HmacFields.UriQuery, HmacFields.HttpHeaders, HmacFields.Payload); + EnsureSignedHeaders(out var signedHeaders); + var signedHeadersLookup = signedHeaders.Split(';').ToList(); + var headersToSign = DelimitedString.Create(Data[HmacFields.HttpHeaders].Split(Alphanumeric.Linefeed.ToCharArray()).Where(header => + { + var kvp = header.Split(':'); + return signedHeadersLookup.Contains(kvp[0]); + }), o => o.Delimiter = Alphanumeric.Linefeed) + Alphanumeric.Linefeed; + + var stringToSign = new StringBuilder(Data[HmacFields.HttpMethod]) + .Append(Alphanumeric.Linefeed) + .Append(Data[HmacFields.UriPath]) + .Append(Alphanumeric.Linefeed) + .Append(Data[HmacFields.UriQuery]) + .Append(Alphanumeric.Linefeed) + .Append(headersToSign) + .Append(Alphanumeric.Linefeed) + .Append(signedHeaders) + .Append(Alphanumeric.Linefeed) + .Append(Data[HmacFields.Payload]).ToString(); + + return UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(stringToSign).ToHexadecimalString(); + } + + /// + /// Computes the signature of this instance using a series of hash-based message authentication codes (HMACs) using . + /// + /// A representation, in hexadecimal, of the computed signature of this instance. + public virtual string ComputeSignature() + { + ValidateData(HmacFields.ServerDateTime, HmacFields.ClientSecret); + var secret = Decorator.Enclose(Data[HmacFields.ClientSecret]).ToByteArray(); + var stringToSign = string.Concat(Algorithm, + Alphanumeric.Linefeed, + Data[HmacFields.ServerDateTime], + Alphanumeric.Linefeed, + Decorator.Enclose(Data).GetValueOrDefault(HmacFields.CredentialScope), + Alphanumeric.Linefeed, + ComputeCanonicalRequest()); + var date = DateTime.Parse(Data[HmacFields.ServerDateTime], CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind).Date.ToString("yyyyMMdd"); + var dateSecret = KeyedHashFactory.CreateHmacCrypto(secret, HmacAlgorithm).ComputeHash(date).GetBytes(); + return KeyedHashFactory.CreateHmacCrypto(dateSecret, HmacAlgorithm).ComputeHash(stringToSign).ToHexadecimalString(); + } + + /// + /// Builds an instance of that implements . + /// + /// An instance of . + public override HmacAuthorizationHeader Build() + { + ValidateData(HmacFields.ClientId, HmacFields.ServerDateTime, HmacFields.ClientSecret, HmacFields.UriPath, HmacFields.UriQuery, HmacFields.HttpHeaders, HmacFields.Payload); + EnsureSignedHeaders(out var signedHeaders); + return new HmacAuthorizationHeader(Data[HmacFields.ClientId], Decorator.Enclose(Data).GetValueOrDefault(HmacFields.CredentialScope), signedHeaders, ComputeSignature(), AuthenticationScheme); + } + + private void EnsureSignedHeaders(out string signedHeaders) + { + if (!Data.TryGetValue(HmacFields.SignedHeaders, out signedHeaders)) + { + signedHeaders = "host;date"; + AddSignedHeaders(signedHeaders); + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Hmac/HmacFields.cs b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacFields.cs new file mode 100644 index 000000000..ec3bd782d --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacFields.cs @@ -0,0 +1,58 @@ +namespace Cuemon.AspNetCore.Authentication.Hmac +{ + /// + /// A collection of constants for . + /// + public static class HmacFields + { + /// + /// The HTTP request method. + /// + public const string HttpMethod = "httpRequestMethod"; + + /// + /// The canonical URI that is the URI-encoded version of the absolute path component of an URI. + /// + public const string UriPath = "canonicalUri"; + + /// + /// The canonical query string. + /// + public const string UriQuery = "canonicalQueryString"; + + /// + /// The canonical headers. + /// + public const string HttpHeaders = "canonicalHeaders"; + + /// + /// The headers that must be part of the signing process. + /// + public const string SignedHeaders = "signedHeaders"; + + /// + /// The request payload. + /// + public const string Payload = "requestPayload"; + + /// + /// The server date time expressed in ISO 8601 format. + /// + public const string ServerDateTime = "serverDateTime"; + + /// + /// The public key of the signing process. + /// + public const string ClientId = "clientId"; + + /// + /// The private key of the signing process. + /// + public const string ClientSecret = "clientSecret"; + + /// + /// The credential scope that defines the remote resource. + /// + public const string CredentialScope = "credentialScope"; + } +} \ No newline at end of file From 5a062b54547744e5377683ce770f1ebd402fee0b Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 22 Feb 2021 04:40:01 +0100 Subject: [PATCH 358/385] Refactored to use new base classes. --- .../Digest/DigestAuthenticationMiddleware.cs | 134 +++++++++ .../Digest/DigestAuthenticationOptions.cs | 134 +++++++++ .../Digest/DigestAuthenticator.cs | 12 + .../Digest/DigestAuthorizationHeader.cs | 182 +++++++++++++ .../DigestAuthorizationHeaderBuilder.cs | 257 ++++++++++++++++++ .../Digest/DigestFields.cs | 63 +++++ 6 files changed, 782 insertions(+) create mode 100644 src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationMiddleware.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationOptions.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticator.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthorizationHeader.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthorizationHeaderBuilder.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Digest/DigestFields.cs diff --git a/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationMiddleware.cs new file mode 100644 index 000000000..00a16084e --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationMiddleware.cs @@ -0,0 +1,134 @@ +using System; +using System.Security.Claims; +using System.Threading.Tasks; +using Cuemon.IO; +using Cuemon.Security.Cryptography; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; + +namespace Cuemon.AspNetCore.Authentication.Digest +{ + /// + /// Provides a HTTP Digest Access Authentication middleware implementation for ASP.NET Core. + /// + public class DigestAuthenticationMiddleware : ConfigurableMiddleware + { + private INonceTracker _nonceTracker; + + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + /// The which need to be configured. + public DigestAuthenticationMiddleware(RequestDelegate next, IOptions setup) : base(next, setup) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + /// The middleware which need to be configured. + public DigestAuthenticationMiddleware(RequestDelegate next, Action setup) : base(next, setup) + { + } + + /// + /// Executes the . + /// + /// The context of the current request. + /// The dependency injected implementation of an . + /// A task that represents the execution of this middleware. + public override async Task InvokeAsync(HttpContext context, INonceTracker nonceTracker) + { + _nonceTracker = nonceTracker; + if (!Authenticator.TryAuthenticate(context, Options.RequireSecureConnection, AuthorizationHeaderParser, TryAuthenticate)) + { + await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (message, response) => + { + context.Response.OnStarting(() => + { + string etag = context.Response.Headers[HeaderNames.ETag]; + if (string.IsNullOrEmpty(etag)) { etag = "no-entity-tag"; } + var opaqueGenerator = Options.OpaqueGenerator; + var nonceSecret = Options.NonceSecret; + var nonceGenerator = Options.NonceGenerator; + var staleNonce = context.Items[DigestFields.Stale] as string ?? "false"; + context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{DigestAuthorizationHeader.Scheme} realm=\"{Options.Realm}\", qop=\"auth, auth-int\", nonce=\"{nonceGenerator(DateTime.UtcNow, etag, nonceSecret())}\", opaque=\"{opaqueGenerator()}\", stale=\"{staleNonce}\", algorithm=\"{ParseAlgorithm(Options.Algorithm)}\"")); + return Task.CompletedTask; + }); + response.StatusCode = (int)message.StatusCode; + await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); + }).ConfigureAwait(false); + } + await Next.Invoke(context).ConfigureAwait(false); + } + + private bool TryAuthenticate(HttpContext context, DigestAuthorizationHeader header, out ClaimsPrincipal result) + { + if (Options.Authenticator == null) { throw new InvalidOperationException(FormattableString.Invariant($"The {nameof(Options.Authenticator)} delegate cannot be null.")); } + + if (header == null) + { + result = null; + return false; + } + + result = null; + var nonceExpiredParser = Options.NonceExpiredParser; + var staleNonce = nonceExpiredParser(header.Nonce, TimeSpan.FromSeconds(30)); + if (staleNonce) + { + context.Items.Add(DigestFields.Stale, "true"); + return false; + } + + if (_nonceTracker != null) + { + var nc = Convert.ToInt32(header.NC, 16); + if (_nonceTracker.TryGetEntry(header.Nonce, out var previousNonce)) + { + if (previousNonce.Count == nc) + { + context.Items.Add(DigestFields.Stale, "true"); + return false; + } + } + else + { + _nonceTracker.TryAddEntry(header.Nonce, nc); + } + } + + result = Options.Authenticator(header.UserName, out var password); + + var db = new DigestAuthorizationHeaderBuilder().AddFromDigestAuthorizationHeader(header); + var ha1 = db.ComputeHash1(password); + var ha2 = db.ComputeHash2(context.Request.Method, Decorator.Enclose(context.Request.Body).ToEncodedString(o => o.LeaveOpen = true)); + var serverResponse = db.ComputeResponse(ha1, ha2); + + return serverResponse != null && serverResponse.Equals(header.Response, StringComparison.Ordinal) && Condition.IsNotNull(result); + } + + private DigestAuthorizationHeader AuthorizationHeaderParser(HttpContext context, string authorizationHeader) + { + return DigestAuthorizationHeader.Create(authorizationHeader); + } + + + + private static string ParseAlgorithm(UnkeyedCryptoAlgorithm algorithm) + { + switch (algorithm) + { + case UnkeyedCryptoAlgorithm.Sha256: + return "SHA-256"; + case UnkeyedCryptoAlgorithm.Sha512: + return "SHA-512-256"; + default: + return "MD5"; + } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationOptions.cs new file mode 100644 index 000000000..9879a5146 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationOptions.cs @@ -0,0 +1,134 @@ +using System; +using System.Globalization; +using System.Text; +using Cuemon.Security.Cryptography; +using Cuemon.Text; + +namespace Cuemon.AspNetCore.Authentication.Digest +{ + /// + /// Configuration options for . This class cannot be inherited. + /// + /// + public sealed class DigestAuthenticationOptions : AuthenticationOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// + /// + /// + /// + /// null + /// + /// + /// + /// A default implementation of a nonce generator. + /// + /// + /// + /// A default implementation of an opaque generator. + /// + /// + /// + /// A default implementation of a nonce expiry parser. + /// + /// + /// + /// A default secret to get you started without overwhelming configuration. Do change when moving outside a development environment. + /// + /// + /// + /// AuthenticationServer + /// + /// + /// + public DigestAuthenticationOptions() + { + Algorithm = UnkeyedCryptoAlgorithm.Sha256; + OpaqueGenerator = () => Generate.RandomString(32, Alphanumeric.Hexadecimal).ToLowerInvariant(); + NonceExpiredParser = (nonce, timeToLive) => + { + Validator.ThrowIfNullOrEmpty(nonce, nameof(nonce)); + if (ParserFactory.FromBase64().TryParse(nonce, out var rawNonce)) + { + var nonceProtocol = Convertible.ToString(rawNonce, options => + { + options.Encoding = Encoding.UTF8; + options.Preamble = PreambleSequence.Remove; + }); + var nonceTimestamp = DateTime.ParseExact(nonceProtocol.Substring(0, nonceProtocol.LastIndexOf(':')), "u", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); + var difference = (DateTime.UtcNow - nonceTimestamp); + return (difference > timeToLive); + } + return false; + }; + NonceGenerator = (timestamp, entityTag, privateKey) => + { + Validator.ThrowIfNullOrWhitespace(entityTag, nameof(entityTag)); + Validator.ThrowIfNull(privateKey, nameof(privateKey)); + var nonceHash = UnkeyedHashFactory.CreateCryptoSha256().ComputeHash(timestamp.Ticks, entityTag, Convert.ToBase64String(privateKey)).ToHexadecimalString(); + var nonceProtocol = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", timestamp.ToString("u", CultureInfo.InvariantCulture), nonceHash); + return Convert.ToBase64String(Convertible.GetBytes(nonceProtocol, options => + { + options.Encoding = Encoding.UTF8; + options.Preamble = PreambleSequence.Remove; + })); + }; + NonceSecret = () => Convert.FromBase64String("ZHBGWDRrVGVxbFlhVEpWQ3hoYUc5VUlZM05penNOaUk="); + Realm = "AuthenticationServer"; + } + + /// + /// Gets or sets the function delegate that will perform the authentication from the specified username. + /// + /// The function delegate that will perform the authentication. + public DigestAuthenticator Authenticator { get; set; } + + /// + /// Gets or sets the algorithm of the HTTP Digest Access Authentication. Default is . + /// + /// The algorithm of the HTTP Digest Access Authentication. + /// Allowed values are: , and . + public UnkeyedCryptoAlgorithm Algorithm { get; set; } + + /// + /// Gets the realm that defines the protection space. + /// + /// The realm that defines the protection space. + public string Realm { get; set; } + + /// + /// Gets or sets the function delegate for generating opaque string values. + /// + /// The function delegate for generating opaque string values. + public Func OpaqueGenerator { get; set; } + + /// + /// Gets or sets the function delegate for retrieving the cryptographic secret used in nonce string values. + /// + /// The function delegate for retrieving the cryptographic secret used in nonce string values. + public Func NonceSecret { get; set; } + + /// + /// Gets or sets the function delegate for generating nonce string values. + /// + /// The function delegate for generating nonce string values. + public Func NonceGenerator { get; set; } + + /// + /// Gets or sets the function delegate for parsing nonce string values for expiration. + /// + /// The function delegate for parsing nonce string values for expiration. + public Func NonceExpiredParser { get; set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticator.cs b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticator.cs new file mode 100644 index 000000000..5ade72881 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticator.cs @@ -0,0 +1,12 @@ +using System.Security.Claims; + +namespace Cuemon.AspNetCore.Authentication.Digest +{ + /// + /// Represents the method that defines an Authenticator typically assigned on . + /// + /// The username to match and lookup the paired . + /// The password paired with . + /// A that is associated with the result of . + public delegate ClaimsPrincipal DigestAuthenticator(string username, out string password); +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthorizationHeader.cs b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthorizationHeader.cs new file mode 100644 index 000000000..5be734edd --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthorizationHeader.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Cuemon.AspNetCore.Authentication.Digest +{ + /// + /// Provides a representation of a HTTP Digest Access Authentication header. + /// Implements the + /// + /// + public class DigestAuthorizationHeader : AuthorizationHeader + { + /// + /// Creates an instance of from the specified parameters. + /// + /// The raw HTTP authorization header. + /// An instance of . + /// + /// cannot be null. + /// + /// + /// cannot be empty or consist only of white-space characters. + /// + public static DigestAuthorizationHeader Create(string authorizationHeader) + { + Validator.ThrowIfNullOrWhitespace(authorizationHeader, nameof(authorizationHeader)); + return new DigestAuthorizationHeader().Parse(authorizationHeader, o => o.CredentialsDelimiter = " ") as DigestAuthorizationHeader; + } + + /// + /// The authentication scheme of the . + /// + public const string Scheme = "Digest"; + + DigestAuthorizationHeader() : base(Scheme) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The realm/credential scope that defines the remote resource. + /// The unique server generated string. + /// The string of data specified by the server. + /// The case-insensitive flag indicating if the previous request from the client was rejected because the value was stale. + /// The algorithm used to produce the digest and an unkeyed digest. + /// The username of the specified . + /// The effective request URI. + /// The hexadecimal count of the number of requests the client has sent with the value. + /// The unique client generated string. + /// The "quality of protection" the client has applied to the message. + /// The computed response which proves that the user knows a password. + public DigestAuthorizationHeader(string realm, string nonce, string opaque, string stale, string algorithm, string userName, string uri, string nc, string cNonce, string qop, string response) : base(Scheme) + { + Realm = realm; + Nonce = nonce; + Opaque = opaque; + Stale = stale; + Algorithm = algorithm; + UserName = userName; + Uri = uri; + NC = nc; + CNonce = cNonce; + Qop = qop; + Response = response; + } + + /// + /// Gets the realm/credential scope that defines the remote resource. + /// + /// The realm/credential scope that defines the remote resource. + public string Realm { get; } + + /// + /// Gets the unique server generated string. + /// + /// The unique server generated string. + public string Nonce { get; } + + /// + /// Gets the string of data specified by the server. + /// + /// The string of data specified by the server. + public string Opaque { get; } + + /// + /// Gets the algorithm used to produce the digest and an unkeyed digest. + /// + /// The algorithm used to produce the digest and an unkeyed digest. + public string Algorithm { get; } + + /// + /// Gets the username of the specified . + /// + /// The username of the specified . + public string UserName { get; } + + /// + /// Gets the effective request URI. + /// + /// The effective request URI. + public string Uri { get; } + + /// + /// Gets the computed response which proves that the user knows a password. + /// + /// The computed response which proves that the user knows a password. + public string Response { get; } + + /// + /// Gets the "quality of protection" the client has applied to the message. + /// + /// The "quality of protection" the client has applied to the message. + public string Qop { get; } + + /// + /// Gets the unique client generated string. + /// + /// The unique client generated string. + public string CNonce { get; } + + /// + /// Gets the hexadecimal count of the number of requests the client has sent with the value. + /// + /// The hexadecimal count of the number of requests the client has sent with the value. + public string NC { get; } + + /// + /// Gets the case-insensitive flag indicating if the previous request from the client was rejected because the value was stale. + /// + /// The case-insensitive flag indicating if the previous request from the client was rejected because the value was stale. + public string Stale { get; } + + /// + /// The core parser that resolves an from a set of . + /// + /// The credentials used in authentication. + /// An equivalent of . + protected override AuthorizationHeader ParseCore(IReadOnlyDictionary credentials) + { + var valid = credentials.TryGetValue(DigestFields.Realm, out var realm); + valid |= credentials.TryGetValue(DigestFields.Nonce, out var nonce); + valid |= credentials.TryGetValue(DigestFields.Opaque, out var opaque); + valid |= credentials.TryGetValue(DigestFields.Algorithm, out var algorithm); + valid |= credentials.TryGetValue(DigestFields.UserName, out var userName); + valid |= credentials.TryGetValue(DigestFields.DigestUri, out var uri); + valid |= credentials.TryGetValue(DigestFields.Response, out var response); + valid |= credentials.TryGetValue(DigestFields.QualityOfProtection, out var qop); + valid |= credentials.TryGetValue(DigestFields.ClientNonce, out var cnonce); + valid |= credentials.TryGetValue(DigestFields.NonceCount, out var nc); + valid |= credentials.TryGetValue(DigestFields.Stale, out var stale); + return valid ? new DigestAuthorizationHeader(realm, nonce, opaque, stale, algorithm, userName, uri, nc, cnonce, qop, response) : null; + } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + var sb = new StringBuilder(AuthenticationScheme); + AppendField(sb, DigestFields.UserName, UserName); + AppendField(sb, DigestFields.Realm, Realm); + AppendField(sb, DigestFields.Nonce, Nonce); + AppendField(sb, DigestFields.DigestUri, Uri); + AppendField(sb, DigestFields.QualityOfProtection, Qop); + AppendField(sb, DigestFields.NonceCount, NC); + AppendField(sb, DigestFields.ClientNonce, CNonce); + AppendField(sb, DigestFields.Response, Response); + AppendField(sb, DigestFields.Opaque, Opaque); + AppendField(sb, DigestFields.Stale, Stale); + AppendField(sb, DigestFields.Algorithm, Algorithm); + return sb.ToString(); + } + + private void AppendField(StringBuilder sb, string fn, string fv) + { + if (!string.IsNullOrWhiteSpace(fv)) { sb.Append($" {fn}=\"{fv}\""); } + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthorizationHeaderBuilder.cs b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthorizationHeaderBuilder.cs new file mode 100644 index 000000000..856110eb0 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthorizationHeaderBuilder.cs @@ -0,0 +1,257 @@ +using System; +using System.Globalization; +using System.Text; +using Cuemon.Security.Cryptography; +using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; + +namespace Cuemon.AspNetCore.Authentication.Digest +{ + /// + /// Provides a way to fluently represent a HTTP Digest Access Authentication header. + /// + public class DigestAuthorizationHeaderBuilder : AuthorizationHeaderBuilder + { + /// + /// Initializes a new instance of the class. + /// + /// The algorithm to use when computing HA1, HA2 and/or RESPONSE value(s). + /// Allowed values for are: , and . + public DigestAuthorizationHeaderBuilder(UnkeyedCryptoAlgorithm algorithm = UnkeyedCryptoAlgorithm.Sha256) : base(DigestAuthorizationHeader.Scheme) + { + Validator.ThrowIfEqual(algorithm, UnkeyedCryptoAlgorithm.Sha1, nameof(algorithm)); + Validator.ThrowIfEqual(algorithm, UnkeyedCryptoAlgorithm.Sha384, nameof(algorithm)); + Algorithm = algorithm; + MapRelation(nameof(AddResponse), DigestFields.Response); + MapRelation(nameof(AddRealm), DigestFields.Realm); + MapRelation(nameof(AddUserName), DigestFields.UserName); + MapRelation(nameof(AddUri), DigestFields.DigestUri); + MapRelation(nameof(AddNc), DigestFields.NonceCount); + MapRelation(nameof(AddCnonce), DigestFields.ClientNonce); + MapRelation(nameof(AddQopAuthentication), DigestFields.QualityOfProtection); + MapRelation(nameof(AddQopAuthenticationIntegrity), DigestFields.QualityOfProtection); + MapRelation(nameof(ComputeHash1), DigestFields.UserName, DigestFields.Realm); + MapRelation(nameof(ComputeHash2), DigestFields.QualityOfProtection, DigestFields.DigestUri); + MapRelation(nameof(ComputeResponse), DigestFields.Nonce, DigestFields.NonceCount, DigestFields.ClientNonce, DigestFields.QualityOfProtection); + } + + /// + /// Gets the algorithm of the HTTP Digest Access Authentication. + /// + /// The algorithm of the HTTP Digest Access Authentication. + public UnkeyedCryptoAlgorithm Algorithm { get; private set; } + + /// + /// Associates the field with the specified . + /// + /// The realm that defines the remote resource. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestAuthorizationHeaderBuilder AddRealm(string realm) + { + return AddOrUpdate(DigestFields.Realm, realm); + } + + /// + /// Associates the field with the specified . + /// + /// The username to use in the authentication process. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestAuthorizationHeaderBuilder AddUserName(string username) + { + Validator.ThrowIfNullOrWhitespace(username, nameof(username)); + return AddOrUpdate(DigestFields.UserName, username); + } + + /// + /// Associates the field with the specified . + /// + /// The effective request URI to use in the authentication process. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestAuthorizationHeaderBuilder AddUri(string digestUri) + { + Validator.ThrowIfNullOrWhitespace(digestUri, nameof(digestUri)); + return AddOrUpdate(DigestFields.DigestUri, digestUri); + } + + /// + /// Associates the field with the specified . + /// + /// The count of the number of requests to use in the authentication process. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestAuthorizationHeaderBuilder AddNc(int nonceCount) + { + Validator.ThrowIfLowerThan(nonceCount, 0, nameof(nonceCount)); + return AddOrUpdate(DigestFields.NonceCount, nonceCount.ToString("x8")); + } + + /// + /// Associates the field with the specified . + /// + /// The cryptographic client nonce to use in the authentication process. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestAuthorizationHeaderBuilder AddCnonce(string clientNonce = null) + { + if (clientNonce == null) { clientNonce = Generate.RandomString(32); } + return AddOrUpdate(DigestFields.ClientNonce, clientNonce); + } + + /// + /// Associates the field with "auth". + /// + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestAuthorizationHeaderBuilder AddQopAuthentication() + { + return AddOrUpdate(DigestFields.QualityOfProtection, "auth"); + } + + /// + /// Associates the field with "auth-int". + /// + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestAuthorizationHeaderBuilder AddQopAuthenticationIntegrity() + { + return AddOrUpdate(DigestFields.QualityOfProtection, "auth-int"); + } + + /// + /// Associates any Digest fields found in the HTTP WWW-Authenticate header from the specified . + /// + /// An implementation of . + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestAuthorizationHeaderBuilder AddFromWwwAuthenticateHeader(IHeaderDictionary headers) + { + Validator.ThrowIfNull(headers, nameof(headers)); + string header = headers[HeaderNames.WWWAuthenticate]; + Validator.ThrowIfFalse(() => header.StartsWith(AuthenticationScheme), nameof(header), $"Header did not start with {AuthenticationScheme}."); + var headerWithoutScheme = header.Remove(0, AuthenticationScheme.Length + 1); + + var fields = DelimitedString.Split(headerWithoutScheme); + foreach (var field in fields) + { + var kvp = DelimitedString.Split(field, o => o.Delimiter = "="); + var key = kvp[0].Trim(); + var value = kvp[1].Trim('"'); + if (key == DigestFields.QualityOfProtection) { continue; } + AddOrUpdate(key, value); + } + return this; + } + + /// + /// Associates any Digest fields found in the HTTP Authorization header from the specified . + /// + /// An instance of . + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestAuthorizationHeaderBuilder AddFromDigestAuthorizationHeader(DigestAuthorizationHeader header) + { + Validator.ThrowIfNull(header, nameof(header)); + Algorithm = ParseAlgorithm(header.Algorithm); + AddUserName(header.UserName); + AddRealm(header.Realm); + AddUri(header.Uri); + AddNc(Convert.ToInt32(header.NC, 16)); + AddCnonce(header.CNonce); + AddOrUpdate(DigestFields.Nonce, header.Nonce); + Condition.FlipFlop(header.Qop.Equals("auth-int", StringComparison.OrdinalIgnoreCase), () => AddQopAuthenticationIntegrity(), () => AddQopAuthentication()); + return this; + } + + private static UnkeyedCryptoAlgorithm ParseAlgorithm(string algorithm) + { + return algorithm.StartsWith("SHA-512", StringComparison.OrdinalIgnoreCase) ? UnkeyedCryptoAlgorithm.Sha512 + : algorithm.StartsWith("SHA-256", StringComparison.OrdinalIgnoreCase) ? UnkeyedCryptoAlgorithm.Sha256 + : UnkeyedCryptoAlgorithm.Md5; + } + + /// + /// Associates the field with the computed values of , and . + /// + /// The password to include in the HA1 computed value. + /// The HTTP method to include in the HA2 computed value. + /// The entity body to apply in the signature when qop is set to auth-int. + /// An that can be used to further build the HTTP Digest Access Authentication header. + public DigestAuthorizationHeaderBuilder AddResponse(string password, string method, string entityBody = null) + { + Validator.ThrowIfNullOrWhitespace(password, nameof(password)); + Validator.ThrowIfNullOrWhitespace(method, nameof(method)); + ValidateData(DigestFields.UserName, DigestFields.Realm, DigestFields.QualityOfProtection, DigestFields.DigestUri, DigestFields.Nonce, DigestFields.NonceCount, DigestFields.ClientNonce); + return AddOrUpdate(DigestFields.Response, ComputeResponse(ComputeHash1(password), ComputeHash2(method, entityBody))); + } + + /// + /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA1. + /// + /// The password to include in the HA1 computed value. + /// A in the format of H(::). H is determined by . + public virtual string ComputeHash1(string password) + { + Validator.ThrowIfNullOrWhitespace(password, nameof(password)); + ValidateData(DigestFields.UserName, DigestFields.Realm); + return UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(string.Format(CultureInfo.InvariantCulture, "{0}:{1}:{2}", Data[DigestFields.UserName], Data[DigestFields.Realm], password), o => + { + o.Encoding = Encoding.UTF8; + }).ToHexadecimalString(); + } + + /// + /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication HA2. + /// + /// The HTTP method to include in the HA2 computed value. + /// The entity body to apply in the signature when qop is set to auth-int. + /// A in the format of H(:) OR H(::H()). H is determined by . + public virtual string ComputeHash2(string method, string entityBody = null) + { + Validator.ThrowIfNullOrWhitespace(method, nameof(method)); + method = method.ToUpperInvariant(); + ValidateData(DigestFields.QualityOfProtection, DigestFields.DigestUri); + var qop = Data[DigestFields.QualityOfProtection]; + var hasIntegrityProtection = qop.Equals("auth-int", StringComparison.OrdinalIgnoreCase); + if (hasIntegrityProtection && entityBody == null) { throw new ArgumentNullException(nameof(entityBody), "The entity body cannot be null when qop is set to auth-int."); } + + var hashFields = !hasIntegrityProtection + ? FormattableString.Invariant($"{method}:{Data[DigestFields.DigestUri]}") + : FormattableString.Invariant($"{method}:{Data[DigestFields.DigestUri]}:{UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(entityBody, o => o.Encoding = Encoding.UTF8).ToHexadecimalString()}"); + return UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(hashFields, o => + { + o.Encoding = Encoding.UTF8; + }).ToHexadecimalString(); + } + + /// + /// Computes a by parameter defined hash value of the required values for the HTTP Digest access authentication RESPONSE. + /// + /// The HA1 to include in the RESPONSE computed value. + /// The HA2 to include in the RESPONSE computed value. + /// A in the format of H(:::::). H is determined by . + public virtual string ComputeResponse(string hash1, string hash2) + { + Validator.ThrowIfNullOrWhitespace(hash1, nameof(hash1)); + Validator.ThrowIfNullOrWhitespace(hash2, nameof(hash2)); + ValidateData(DigestFields.Nonce, DigestFields.NonceCount, DigestFields.ClientNonce, DigestFields.QualityOfProtection); + return UnkeyedHashFactory.CreateCrypto(Algorithm).ComputeHash(FormattableString.Invariant($"{hash1}:{Data[DigestFields.Nonce]}:{Data[DigestFields.NonceCount]}:{Data[DigestFields.ClientNonce]}:{Data[DigestFields.QualityOfProtection]}:{hash2}"), o => + { + o.Encoding = Encoding.UTF8; + }).ToHexadecimalString(); + } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override DigestAuthorizationHeader Build() + { + ValidateData(DigestFields.Realm, DigestFields.Nonce, DigestFields.UserName, DigestFields.QualityOfProtection, DigestFields.DigestUri, DigestFields.NonceCount, DigestFields.ClientNonce, DigestFields.QualityOfProtection, DigestFields.Response); + return new DigestAuthorizationHeader(Data[DigestFields.Realm], + Data[DigestFields.Nonce], + Data[DigestFields.Opaque], + Data[DigestFields.Stale], + Data[DigestFields.Algorithm], + Data[DigestFields.UserName], + Data[DigestFields.DigestUri], + Data[DigestFields.NonceCount], + Data[DigestFields.ClientNonce], + Data[DigestFields.QualityOfProtection], + Data[DigestFields.Response]); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Digest/DigestFields.cs b/src/Cuemon.AspNetCore.Authentication/Digest/DigestFields.cs new file mode 100644 index 000000000..b80d423e1 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Digest/DigestFields.cs @@ -0,0 +1,63 @@ +namespace Cuemon.AspNetCore.Authentication.Digest +{ + /// + /// A collection of constants for . + /// + public static class DigestFields + { + /// + /// The username field of a HTTP Digest access authentication. + /// + public const string UserName = "username"; + + /// + /// The realm field of a HTTP Digest access authentication. + /// + public const string Realm = "realm"; + + /// + /// The response field of a HTTP Digest access authentication. + /// + public const string Response = "response"; + + /// + /// The qop (quality of protection) field of a HTTP Digest access authentication. + /// + public const string QualityOfProtection = "qop"; + + /// + /// The client nonce (cnonce) field of a HTTP Digest access authentication. + /// + public const string ClientNonce = "cnonce"; + + /// + /// The nc (nonce count) field of a HTTP Digest access authentication. + /// + public const string NonceCount = "nc"; + + /// + /// The nonce field of a HTTP Digest access authentication. + /// + public const string Nonce = "nonce"; + + /// + /// The uri (digest URI) field of a HTTP Digest access authentication. + /// + public const string DigestUri = "uri"; + + /// + /// The opaque field of a HTTP Digest access authentication. + /// + public const string Opaque = "opaque"; + + /// + /// The algorithm field of a HTTP Digest access authentication. + /// + public const string Algorithm = "algorithm"; + + /// + /// The stale field of a HTTP Digest access authentication. + /// + public const string Stale = "stale"; + } +} \ No newline at end of file From e3beab73ec036f6f6bb6f4897da0140e889c28d6 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 22 Feb 2021 04:40:41 +0100 Subject: [PATCH 359/385] Moved; will be refactored ASAP. --- .../Basic/BasicAuthenticationMiddleware.cs | 93 +++++++++++++++++++ .../Basic/BasicAuthenticationOptions.cs | 28 ++++++ .../Basic/BasicAuthenticator.cs | 12 +++ 3 files changed, 133 insertions(+) create mode 100644 src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationMiddleware.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationOptions.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticator.cs diff --git a/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationMiddleware.cs new file mode 100644 index 000000000..87a416985 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationMiddleware.cs @@ -0,0 +1,93 @@ +using System; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; +using Cuemon.IO; +using Cuemon.Text; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; + +namespace Cuemon.AspNetCore.Authentication.Basic +{ + /// + /// Provides a HTTP Basic Authentication middleware implementation for ASP.NET Core. + /// + public class BasicAuthenticationMiddleware : ConfigurableMiddleware + { + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + /// The which need to be configured. + public BasicAuthenticationMiddleware(RequestDelegate next, IOptions setup) : base(next, setup) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + /// The middleware which need to be configured. + public BasicAuthenticationMiddleware(RequestDelegate next, Action setup) : base(next, setup) + { + } + + /// + /// Executes the . + /// + /// The context of the current request. + /// A task that represents the execution of this middleware. + public override async Task InvokeAsync(HttpContext context) + { + if (!Authenticator.TryAuthenticate(context, Options.RequireSecureConnection, AuthorizationHeaderParser, TryAuthenticate)) + { + await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (message, response) => + { + context.Response.OnStarting(() => + { + context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{AuthenticationScheme} realm=\"{Options.Realm}\"")); + return Task.CompletedTask; + }); + response.StatusCode = (int)message.StatusCode; + await Decorator.Enclose(response.Body).WriteAsync(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ConfigureAwait(false); + }).ConfigureAwait(false); + } + await Next(context).ConfigureAwait(false); + } + + /// + /// Gets the name of the authentication scheme. + /// + /// The name of the authentication scheme. + public string AuthenticationScheme => "Basic"; + + private bool TryAuthenticate(HttpContext context, Template credentials, out ClaimsPrincipal result) + { + if (Options.Authenticator == null) { throw new InvalidOperationException(FormattableString.Invariant($"The {nameof(Options.Authenticator)} cannot be null.")); } + result = Options.Authenticator(credentials.Arg1, credentials.Arg2); + return Condition.IsNotNull(result); + } + + private Template AuthorizationHeaderParser(HttpContext context, string authorizationHeader) + { + if (Authenticator.IsAuthenticationSchemeValid(authorizationHeader, AuthenticationScheme)) + { + var base64Credentials = authorizationHeader.Remove(0, AuthenticationScheme.Length + 1); + if (Condition.IsBase64(base64Credentials)) + { + var credentials = Convertible.ToString(Convert.FromBase64String(base64Credentials), options => + { + options.Encoding = Encoding.ASCII; + options.Preamble = PreambleSequence.Remove; + }).Split(':'); + if (credentials.Length == 2 && + !string.IsNullOrEmpty(credentials[0]) && + !string.IsNullOrEmpty(credentials[1])) + { return Template.CreateTwo(credentials[0], credentials[1]); } + } + } + return null; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationOptions.cs new file mode 100644 index 000000000..20dae0b72 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationOptions.cs @@ -0,0 +1,28 @@ +namespace Cuemon.AspNetCore.Authentication.Basic +{ + /// + /// Configuration options for . This class cannot be inherited. + /// + /// + public sealed class BasicAuthenticationOptions : AuthenticationOptions + { + /// + /// Initializes a new instance of the class. + /// + public BasicAuthenticationOptions() + { + } + + /// + /// Gets or sets the function delegate that will perform the authentication from the specified username and password. + /// + /// The function delegate that will perform the authentication. + public BasicAuthenticator Authenticator { get; set; } + + /// + /// Gets the realm that defines the protection space. + /// + /// The realm that defines the protection space. + public string Realm { get; set; } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticator.cs b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticator.cs new file mode 100644 index 000000000..630644c46 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticator.cs @@ -0,0 +1,12 @@ +using System.Security.Claims; + +namespace Cuemon.AspNetCore.Authentication.Basic +{ + /// + /// Represents the method that defines an Authenticator typically assigned on . + /// + /// The username that must be paired with . + /// The password that must be paired with . + /// A that is associated with the result of and . + public delegate ClaimsPrincipal BasicAuthenticator(string username, string password); +} \ No newline at end of file From c6aefb006275ff6b8ebaa991794fd099fe3badae Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 22 Feb 2021 04:42:58 +0100 Subject: [PATCH 360/385] Initial tests for authentication schemes. --- .../Assets/ExceptionMiddleware.cs | 39 ++++++++ .../BasicAuthenticationMiddlewareTest.cs | 1 + ...igestAccessAuthenticationMiddlewareTest.cs | 83 ++++++++-------- .../HmacAuthenticationMiddlewareTest.cs | 95 +++++++++++++++++++ .../Properties/launchSettings.json | 27 ++++++ 5 files changed, 199 insertions(+), 46 deletions(-) create mode 100644 test/Cuemon.AspNetCore.Authentication.Tests/Assets/ExceptionMiddleware.cs create mode 100644 test/Cuemon.AspNetCore.Authentication.Tests/HmacAuthenticationMiddlewareTest.cs create mode 100644 test/Cuemon.AspNetCore.Mvc.Tests/Properties/launchSettings.json diff --git a/test/Cuemon.AspNetCore.Authentication.Tests/Assets/ExceptionMiddleware.cs b/test/Cuemon.AspNetCore.Authentication.Tests/Assets/ExceptionMiddleware.cs new file mode 100644 index 000000000..09ecd546b --- /dev/null +++ b/test/Cuemon.AspNetCore.Authentication.Tests/Assets/ExceptionMiddleware.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading.Tasks; +using Cuemon.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; + +namespace Cuemon.AspNetCore.Authentication.Assets +{ + public class ExceptionMiddleware : Middleware + { + public ExceptionMiddleware(RequestDelegate next) : base(next) + { + } + + public override async Task InvokeAsync(HttpContext context) + { + try + { + await Next(context); + } + catch (Exception exception) + { + if (exception is ArgumentException) + { + context.Response.StatusCode = StatusCodes.Status400BadRequest; + } + throw; + } + } + } + + public static class ApplicationBuilderExtensions + { + public static IApplicationBuilder UseExceptionMiddleware(this IApplicationBuilder builder) + { + return MiddlewareBuilderFactory.UseMiddleware(builder); + } + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs b/test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs index 998c03efd..59dfc5659 100644 --- a/test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs @@ -1,5 +1,6 @@ using System.Security.Claims; using System.Threading.Tasks; +using Cuemon.AspNetCore.Authentication.Basic; using Cuemon.Collections.Generic; using Cuemon.Extensions.AspNetCore.Authentication; using Cuemon.Extensions.Xunit; diff --git a/test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs b/test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs index 96bc2de4d..5227df1d2 100644 --- a/test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs @@ -1,13 +1,16 @@ -using System.IO; +using System; +using System.IO; using System.Security.Claims; using System.Threading.Tasks; +using Cuemon.AspNetCore.Authentication.Assets; +using Cuemon.AspNetCore.Authentication.Digest; using Cuemon.Collections.Generic; using Cuemon.Extensions; using Cuemon.Extensions.AspNetCore.Authentication; using Cuemon.Extensions.IO; using Cuemon.Extensions.Xunit; using Cuemon.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.IO; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -28,11 +31,12 @@ public async Task InvokeAsync_ShouldNotBeAuthenticated() { using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => { + app.UseExceptionMiddleware(); app.UseFakeHttpResponseTrigger(o => o.ShortCircuitOnStarting = true); app.UseDigestAccessAuthentication(); }, services => { - services.Configure(o => + services.Configure(o => { o.Authenticator = (string username, out string password) => { @@ -54,7 +58,7 @@ public async Task InvokeAsync_ShouldNotBeAuthenticated() })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; - var options = middleware.ServiceProvider.GetRequiredService>(); + var options = middleware.ServiceProvider.GetRequiredService>(); var pipeline = middleware.Application.Build(); var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); @@ -65,14 +69,6 @@ public async Task InvokeAsync_ShouldNotBeAuthenticated() var wwwAuthenticate = context.Response.Headers[HeaderNames.WWWAuthenticate]; TestOutput.WriteLine(wwwAuthenticate); - - var encodedUsernameAndPassword = "Agent:Test".ToByteArray().ToBase64String(); - context.Request.Headers.Add(HeaderNames.Authorization, $"Digest {encodedUsernameAndPassword}"); - - ue = await Assert.ThrowsAsync(async () => await pipeline(context)); - - Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); - Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); } } @@ -85,7 +81,7 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeader( app.UseDigestAccessAuthentication(); }, services => { - services.Configure(o => + services.Configure(o => { o.Authenticator = (string username, out string password) => { @@ -107,7 +103,7 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeader( })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; - var options = middleware.ServiceProvider.GetRequiredService>(); + var options = middleware.ServiceProvider.GetRequiredService>(); var pipeline = middleware.Application.Build(); var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); @@ -120,27 +116,27 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeader( TestOutput.WriteLine(wwwAuthenticate); - var db = new DigestHeaderBuilder(options.Value.Algorithm) + var db = new DigestAuthorizationHeaderBuilder(options.Value.Algorithm) + .AddRealm(options.Value.Realm) .AddUserName("Agent") - .AddRealm("unittest") .AddUri("/") .AddNc(1) .AddCnonce() .AddQopAuthentication() - .AddFromWwwAuthenticateHeader(context.Response); + .AddFromWwwAuthenticateHeader(context.Response.Headers); var ha1 = db.ComputeHash1("Test"); TestOutput.WriteLine(ha1); - var ha2 = db.ComputeHash2("GET", context.Response.Body.ToEncodedString()); + var ha2 = db.ComputeHash2("GET", context.Response.Body.ToEncodedString(o => o.LeaveOpen = true)); var response = db.ComputeResponse(ha1, ha2); - db.AddResponse(response); + db.AddResponse("Test", "GET", context.Response.Body.ToEncodedString()); context.Response.Body = new MemoryStream(); - context.Request.Headers.Add(HeaderNames.Authorization, db.ToString()); + context.Request.Headers.Add(HeaderNames.Authorization, db.Build().ToString()); await pipeline(context); @@ -157,7 +153,7 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeaderN app.UseDigestAccessAuthentication(); }, services => { - services.Configure(o => + services.Configure(o => { o.Authenticator = (string username, out string password) => { @@ -173,20 +169,13 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeaderN }; o.Realm = "unittest"; o.RequireSecureConnection = false; - o.DigestAccessSigner = parameters => - { - var db = new DigestHeaderBuilder(parameters.Algorithm, parameters.Credentials); - var ha1 = parameters.Password; // password is ha1 stored in some storage - var ha2 = db.ComputeHash2(parameters.Method, parameters.EntityBody); - return db.ComputeResponse(ha1, ha2); - }; }); services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); services.AddDigestAccessAuthenticationNonceTracker(); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; - var options = middleware.ServiceProvider.GetRequiredService>(); + var options = middleware.ServiceProvider.GetRequiredService>(); var pipeline = middleware.Application.Build(); var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); @@ -198,27 +187,27 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeaderN TestOutput.WriteLine(wwwAuthenticate); - var db = new DigestHeaderBuilder(options.Value.Algorithm) + var db = new DigestAuthorizationHeaderBuilder(options.Value.Algorithm) + .AddRealm(options.Value.Realm) .AddUserName("Agent") - .AddRealm("unittest") .AddUri("/") .AddNc(1) .AddCnonce() .AddQopAuthentication() - .AddFromWwwAuthenticateHeader(context.Response); + .AddFromWwwAuthenticateHeader(context.Response.Headers); - var ha1 = db.ComputeHash1("Test"); + var ha1 = db.ComputeHash1("a69d6da3eea4fa832dc1c0534863988e550e523f1f786c238951b7ec7abf4d57"); TestOutput.WriteLine(ha1); - var ha2 = db.ComputeHash2("GET", context.Response.Body.ToEncodedString()); + var ha2 = db.ComputeHash2("GET", context.Response.Body.ToEncodedString(o => o.LeaveOpen = true)); var response = db.ComputeResponse(ha1, ha2); - db.AddResponse(response); + db.AddResponse("a69d6da3eea4fa832dc1c0534863988e550e523f1f786c238951b7ec7abf4d57", "GET", context.Response.Body.ToEncodedString()); context.Response.Body = new MemoryStream(); - context.Request.Headers.Add(HeaderNames.Authorization, db.ToString()); + context.Request.Headers.Add(HeaderNames.Authorization, db.Build().ToString()); await pipeline(context); @@ -235,7 +224,7 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeaderW app.UseDigestAccessAuthentication(); }, services => { - services.Configure(o => + services.Configure(o => { o.Authenticator = (string username, out string password) => { @@ -257,7 +246,7 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeaderW })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; - var options = middleware.ServiceProvider.GetRequiredService>(); + var options = middleware.ServiceProvider.GetRequiredService>(); var pipeline = middleware.Application.Build(); var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); @@ -269,33 +258,35 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeaderW TestOutput.WriteLine(wwwAuthenticate); - var db = new DigestHeaderBuilder(options.Value.Algorithm) + var db = new DigestAuthorizationHeaderBuilder(options.Value.Algorithm) + .AddRealm(options.Value.Realm) .AddUserName("Agent") - .AddRealm("unittest") .AddUri("/") .AddNc(1) .AddCnonce() .AddQopAuthenticationIntegrity() - .AddFromWwwAuthenticateHeader(context.Response); + .AddFromWwwAuthenticateHeader(context.Response.Headers); TestOutput.WriteLine("Body:"); - var entityBody = context.Response.Body.ToEncodedString(o => o.LeaveOpen = true); + + var entityBody = "test of entityBody in request"; var ha1 = db.ComputeHash1("Test"); TestOutput.WriteLine("HA1:"); TestOutput.WriteLine(ha1); - var ha2 = db.ComputeHash2("GET", context.Response.Body.ToEncodedString()); + var ha2 = db.ComputeHash2("POST", entityBody); var response = db.ComputeResponse(ha1, ha2); TestOutput.WriteLine("HA2:"); TestOutput.WriteLine(ha2); - db.AddResponse(response); + db.AddResponse("Test", "POST", entityBody); - context.Response.Body = StreamFactory.Create(writer => writer.Write(entityBody)); - context.Request.Headers.Add(HeaderNames.Authorization, db.ToString()); + context.Request.Method = "POST"; + context.Request.Body = new MemoryStream(entityBody.ToByteArray()); + context.Request.Headers.Add(HeaderNames.Authorization, db.Build().ToString()); await pipeline(context); diff --git a/test/Cuemon.AspNetCore.Authentication.Tests/HmacAuthenticationMiddlewareTest.cs b/test/Cuemon.AspNetCore.Authentication.Tests/HmacAuthenticationMiddlewareTest.cs new file mode 100644 index 000000000..c696318b8 --- /dev/null +++ b/test/Cuemon.AspNetCore.Authentication.Tests/HmacAuthenticationMiddlewareTest.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Security.Claims; +using System.Threading.Tasks; +using Cuemon.AspNetCore.Authentication.Hmac; +using Cuemon.Collections.Generic; +using Cuemon.Extensions; +using Cuemon.Extensions.AspNetCore.Authentication; +using Cuemon.Extensions.IO; +using Cuemon.Extensions.Xunit; +using Cuemon.Extensions.Xunit.Hosting.AspNetCore; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; +using Newtonsoft.Json.Serialization; +using Xunit; +using Xunit.Abstractions; + +namespace Cuemon.AspNetCore.Authentication +{ + public class HmacAuthenticationMiddlewareTest : Test + { + public HmacAuthenticationMiddlewareTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeader() + { + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseFakeHttpResponseTrigger(o => o.ShortCircuitOnStarting = true); + app.UseHmacAuthentication(); + }, services => + { + services.Configure(o => + { + o.Authenticator = (string clientId, out string clientSecret) => + { + clientSecret = null; + if (clientId == "Agent-Api") + { + clientSecret = "Test"; + var cp = new ClaimsPrincipal(); + cp.AddIdentity(new ClaimsIdentity(Arguments.Yield(new Claim("Name", "Test Agent")))); + return cp; + } + return null; + }; + o.RequireSecureConnection = false; + }); + services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + + var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + + var wwwAuthenticate = context.Response.Headers[HeaderNames.WWWAuthenticate]; + + + TestOutput.WriteLine(wwwAuthenticate); + + context.Request.Host = new HostString("www.cuemon.net"); + context.Request.Headers.Add(HeaderNames.Date, context.Response.Headers[HeaderNames.Date]); + + var hb = new HmacAuthorizationHeaderBuilder() + .AddFromRequest(context.Request) + .AddClientId("Agent-Api") + .AddClientSecret("Test") + .AddCredentialScope("20150830/us-east-1/iam/aws4_request"); + + var hmacHeader = hb.Build(); + + TestOutput.WriteLine(hmacHeader.ToString()); + TestOutput.WriteLine(""); + TestOutput.WriteLine("--- HmacAuthorizationHeaderBuilder ---"); + TestOutput.WriteLine(hb.ToString()); + + context.Request.Headers.Add(HeaderNames.Authorization, hmacHeader.ToString()); + + await pipeline(context); + + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } + } + } +} \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Mvc.Tests/Properties/launchSettings.json b/test/Cuemon.AspNetCore.Mvc.Tests/Properties/launchSettings.json new file mode 100644 index 000000000..505d8e923 --- /dev/null +++ b/test/Cuemon.AspNetCore.Mvc.Tests/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:61097/", + "sslPort": 44328 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Cuemon.AspNetCore.Mvc.Tests": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file From 1e9ca04adbe7a16154e5f2e7ce39af959af36882 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 22 Feb 2021 04:43:49 +0100 Subject: [PATCH 361/385] Propagate message. --- src/Cuemon.Core/Validator.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Cuemon.Core/Validator.cs b/src/Cuemon.Core/Validator.cs index 5d1f58555..c4a785992 100644 --- a/src/Cuemon.Core/Validator.cs +++ b/src/Cuemon.Core/Validator.cs @@ -310,14 +310,14 @@ public static void ThrowIfSequenceEmpty(IEnumerable value, string paramNam /// /// contains no elements. /// - public static void ThrowIfSequenceNullOrEmpty(IEnumerable value, string paramName, string message = "Value contains no elements.") + public static void ThrowIfSequenceNullOrEmpty(IEnumerable value, string paramName, string message = "Value is either null or contains no elements.") { try { - ThrowIfNull(value, paramName); - ThrowIfSequenceEmpty(value, paramName); + ThrowIfNull(value, paramName, message); + ThrowIfSequenceEmpty(value, paramName, message); } - catch (ArgumentException ex) + catch (Exception ex) when (ex is ArgumentException) { throw ExceptionInsights.Embed(ex, MethodBase.GetCurrentMethod(), Arguments.ToArray(value, paramName, message)); } From 146ac92a63b4228cb0d345c636aa58b4e5b9dcf8 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 22 Feb 2021 04:45:06 +0100 Subject: [PATCH 362/385] Rename refactoring. --- .../ApplicationBuilderExtensions.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Cuemon.Extensions.AspNetCore.Authentication/ApplicationBuilderExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Authentication/ApplicationBuilderExtensions.cs index d4ede1a03..2440e9715 100644 --- a/src/Cuemon.Extensions.AspNetCore.Authentication/ApplicationBuilderExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Authentication/ApplicationBuilderExtensions.cs @@ -1,5 +1,7 @@ using System; -using Cuemon.AspNetCore.Authentication; +using Cuemon.AspNetCore.Authentication.Basic; +using Cuemon.AspNetCore.Authentication.Digest; +using Cuemon.AspNetCore.Authentication.Hmac; using Cuemon.AspNetCore.Builder; using Microsoft.AspNetCore.Builder; @@ -25,11 +27,11 @@ public static IApplicationBuilder UseBasicAuthentication(this IApplicationBuilde /// Adds a HTTP Digest Authentication scheme to the request execution pipeline. /// /// The type that provides the mechanisms to configure an application’s request pipeline. - /// The HTTP middleware which may be configured. + /// The HTTP middleware which may be configured. /// A reference to after the operation has completed. - public static IApplicationBuilder UseDigestAccessAuthentication(this IApplicationBuilder builder, Action setup = null) + public static IApplicationBuilder UseDigestAccessAuthentication(this IApplicationBuilder builder, Action setup = null) { - return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); + return MiddlewareBuilderFactory.UseConfigurableMiddleware(builder, setup); } /// From 3fd51b1627670b1a95a222b37f4f9c6c36bfce4a Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 22 Feb 2021 04:45:38 +0100 Subject: [PATCH 363/385] Add default HTTP header; Date. --- .../Http/Features/FakeHttpResponseFeature.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs index 01ad3590e..dc0a281a6 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http.Features; +using Microsoft.Net.Http.Headers; namespace Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features { @@ -14,6 +15,14 @@ public class FakeHttpResponseFeature : HttpResponseFeature private Func _callback; private object _state; + /// + /// Initializes a new instance of the class. + /// + public FakeHttpResponseFeature() + { + Headers.Add(HeaderNames.Date, DateTime.UtcNow.ToString("R")); + } + /// /// Registers a callback to be invoked just before the response starts. This is the /// last chance to modify the , , or From aea29ccc8a376e05022281c135e48dcdb06b0774 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Mon, 22 Feb 2021 04:46:05 +0100 Subject: [PATCH 364/385] - --- src/Cuemon.Core/Properties/PackageReleaseNotes.txt | 1 + src/Cuemon.Core/Text/Stem.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt index a022958b7..920b79924 100644 --- a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt @@ -4,6 +4,7 @@ Availability: NET Standard 2.0 # Upgrade Steps - To use the earlier built-in support for transient fault handling, please refer to the Cuemon.Resilience namespace, as it has been merged and refactored out of this assembly - To use the earlier built-in support for time-measuring and describing exceptions, please refer to the Cuemon.Diagnostics namespace, as it has been merged and refactored out of this assembly +- To use the earlier built-in support for a computed checksum operation, please refer to the Cuemon.Data.Integrity namespace, as it has been merged and refactored out of this and the Cuemon.Integrity assembly - Any former extension methods of the Cuemon namespace (and related) was either removed completely or merged into there respective Cuemon.Extensions.* namespace equivalent   # Breaking Changes diff --git a/src/Cuemon.Core/Text/Stem.cs b/src/Cuemon.Core/Text/Stem.cs index 3a8b128e6..89b1f68da 100644 --- a/src/Cuemon.Core/Text/Stem.cs +++ b/src/Cuemon.Core/Text/Stem.cs @@ -3,7 +3,7 @@ namespace Cuemon.Text { /// - /// Provides a way to support assigning an stem to a value. + /// Provides a way to support assigning a stem to a value. /// public sealed class Stem { From c92c8ca5e6841caa07aabc111de58bff9dd212bd Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 23 Feb 2021 00:26:38 +0100 Subject: [PATCH 365/385] Added net5.0 support. --- Directory.Build.props | 8 +-- azure-pipelines.yml | 55 +++++-------------- .../Cuemon.AspNetCore.Authentication.csproj | 2 +- .../Cuemon.AspNetCore.Mvc.csproj | 2 +- .../Cuemon.AspNetCore.Razor.csproj | 2 +- .../Cuemon.AspNetCore.csproj | 6 +- src/Cuemon.Core/Cuemon.Core.csproj | 2 +- .../Cuemon.Data.Integrity.csproj | 2 +- .../Cuemon.Data.SqlClient.csproj | 2 +- src/Cuemon.Data/Cuemon.Data.csproj | 2 +- .../Cuemon.Diagnostics.csproj | 2 +- ...xtensions.AspNetCore.Authentication.csproj | 2 +- ...Core.Mvc.Formatters.Newtonsoft.Json.csproj | 2 +- ...sions.AspNetCore.Mvc.Formatters.Xml.csproj | 2 +- .../Cuemon.Extensions.AspNetCore.Mvc.csproj | 2 +- .../Cuemon.Extensions.AspNetCore.csproj | 2 +- ...emon.Extensions.Collections.Generic.csproj | 2 +- ....Extensions.Collections.Specialized.csproj | 2 +- .../Cuemon.Extensions.Core.csproj | 2 +- .../Cuemon.Extensions.Data.Integrity.csproj | 2 +- .../Cuemon.Extensions.Data.csproj | 2 +- ...emon.Extensions.DependencyInjection.csproj | 13 +++-- .../Cuemon.Extensions.Diagnostics.csproj | 2 +- .../Cuemon.Extensions.Hosting.csproj | 12 ++-- .../Cuemon.Extensions.IO.csproj | 2 +- .../Cuemon.Extensions.Net.csproj | 10 +++- .../Http/UriExtensions.cs | 1 + .../Cuemon.Extensions.Newtonsoft.Json.csproj | 2 +- .../Cuemon.Extensions.Reflection.csproj | 2 +- .../Cuemon.Extensions.Runtime.Caching.csproj | 2 +- .../Cuemon.Extensions.Text.csproj | 2 +- .../Cuemon.Extensions.Threading.csproj | 2 +- .../Cuemon.Extensions.Xml.csproj | 2 +- ...nsions.Xunit.Hosting.AspNetCore.Mvc.csproj | 10 +++- ...Extensions.Xunit.Hosting.AspNetCore.csproj | 10 +++- .../Cuemon.Extensions.Xunit.Hosting.csproj | 20 +++++-- .../Cuemon.Extensions.Xunit.csproj | 2 +- src/Cuemon.IO/Cuemon.IO.csproj | 2 +- .../Extensions/StreamDecoratorExtensions.cs | 2 +- src/Cuemon.Net/Cuemon.Net.csproj | 2 +- .../Cuemon.Resilience.csproj | 2 +- .../Cuemon.Runtime.Caching.csproj | 2 +- .../Cuemon.Security.Cryptography.csproj | 2 +- src/Cuemon.Threading/Cuemon.Threading.csproj | 2 +- src/Cuemon.Xml/Cuemon.Xml.csproj | 2 +- .../Cuemon.Data.SqlClient.Tests.csproj | 2 +- .../Cuemon.Extensions.Xunit.Tests.csproj | 4 -- 47 files changed, 112 insertions(+), 109 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9b674d717..96adb4919 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -14,7 +14,7 @@ - Copyright © Geekle 2009-2020. All rights reserved. + Copyright © Geekle 2009-2021. All rights reserved. Michael Mortensen Geekle Cuemon for .NET @@ -53,17 +53,17 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4c60f41e6..f6ae5d8eb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -37,9 +37,9 @@ jobs: version: 2.2.207 - task: UseDotNet@2 - displayName: 'Use .Net Core SDK 3.1.401' + displayName: 'Use .Net Core SDK 5.0.103' inputs: - version: 3.1.401 + version: 5.0.103 - task: DotNetCoreCLI@2 displayName: 'Install NBGV tool' @@ -103,6 +103,15 @@ jobs: sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/**/*opencover.xml sonar.cs.vstest.reportsPaths=$(Agent.TempDirectory)/*.trx + - task: DotNetCoreCLI@2 + displayName: 'Build net5.0 compatible Assemblies' + inputs: + command: 'build' + projects: | + src/**/*.csproj + arguments: '--configuration $(BuildConfiguration) --no-restore --framework net5.0' + workingDirectory: '$(BuildSource)' + - task: DotNetCoreCLI@2 displayName: 'Build netcoreapp3.1 compatible Assemblies' inputs: @@ -140,45 +149,9 @@ jobs: inputs: command: 'build' projects: | - src/**/Cuemon.AspNetCore.csproj - src/**/Cuemon.AspNetCore.Authentication.csproj - src/**/Cuemon.AspNetCore.Mvc.csproj - src/**/Cuemon.AspNetCore.Razor.csproj - src/**/Cuemon.Core.csproj - src/**/Cuemon.Data.csproj - src/**/Cuemon.Data.Integrity.csproj - src/**/Cuemon.Data.SqlClient.csproj - src/**/Cuemon.Diagnostics.csproj - src/**/Cuemon.Extensions.AspNetCore.csproj - src/**/Cuemon.Extensions.AspNetCore.Authentication.csproj - src/**/Cuemon.Extensions.AspNetCore.Mvc.csproj - src/**/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj - src/**/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj - src/**/Cuemon.Extensions.Collections.Generic.csproj - src/**/Cuemon.Extensions.Collections.Specialized.csproj - src/**/Cuemon.Extensions.Core.csproj - src/**/Cuemon.Extensions.Data.csproj - src/**/Cuemon.Extensions.Data.Integrity.csproj - src/**/Cuemon.Extensions.DependencyInjection.csproj - src/**/Cuemon.Extensions.Diagnostics.csproj - src/**/Cuemon.Extensions.Hosting.csproj - src/**/Cuemon.Extensions.IO.csproj - src/**/Cuemon.Extensions.Net.csproj - src/**/Cuemon.Extensions.Newtonsoft.Json.csproj - src/**/Cuemon.Extensions.Reflection.csproj - src/**/Cuemon.Extensions.Runtime.Caching.csproj - src/**/Cuemon.Extensions.Text.csproj - src/**/Cuemon.Extensions.Threading.csproj - src/**/Cuemon.Extensions.Xml.csproj - src/**/Cuemon.Extensions.Xunit.csproj - src/**/Cuemon.Extensions.Xunit.Hosting.csproj - src/**/Cuemon.IO.csproj - src/**/Cuemon.Net.csproj - src/**/Cuemon.Resilience.csproj - src/**/Cuemon.Runtime.Caching.csproj - src/**/Cuemon.Security.Cryptography.csproj - src/**/Cuemon.Threading.csproj - src/**/Cuemon.Xml.csproj + src/**/*.csproj + !src/**/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj + !src/**/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj arguments: '--configuration $(BuildConfiguration) --no-restore --framework netstandard2.0' workingDirectory: '$(BuildSource)' diff --git a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj index 1a1f9965d..71c6b04d8 100644 --- a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj +++ b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;netstandard2.0 + net5.0;netcoreapp3.0;netstandard2.0 a10adf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj index ba8edd4f3..12c9fe0ca 100644 --- a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj +++ b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;netstandard2.0 + net5.0;netcoreapp3.0;netstandard2.0 a20adf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj b/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj index 14e333e79..24ef063c5 100644 --- a/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj +++ b/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;netstandard2.0 + net5.0;netcoreapp3.0;netstandard2.0 a30adf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj index b2ff2fff7..b6bd4d9e1 100644 --- a/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj +++ b/src/Cuemon.AspNetCore/Cuemon.AspNetCore.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;netstandard2.0 + net5.0;netcoreapp3.0;netstandard2.0 a00adf91-e7c7-4cb4-a39d-e1a5374c5602 @@ -12,6 +12,10 @@ configurable-middleware middleware http-exception-descriptor throttling-sentinel-middleware user-agent-sentinel-middleware request-identifier-middleware correlation-identifier-middleware hosting-environment-middleware server-timing cache-busting middleware-builder-factory + + + + diff --git a/src/Cuemon.Core/Cuemon.Core.csproj b/src/Cuemon.Core/Cuemon.Core.csproj index 7c235c1be..8990527b2 100644 --- a/src/Cuemon.Core/Cuemon.Core.csproj +++ b/src/Cuemon.Core/Cuemon.Core.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 000bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj b/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj index 2d8605d5c..bf07a6fd6 100644 --- a/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj +++ b/src/Cuemon.Data.Integrity/Cuemon.Data.Integrity.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net5.0;netstandard2.0 130bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj b/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj index b05f5cd52..873e901f3 100644 --- a/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj +++ b/src/Cuemon.Data.SqlClient/Cuemon.Data.SqlClient.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 030bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Data/Cuemon.Data.csproj b/src/Cuemon.Data/Cuemon.Data.csproj index 9ef24c4b2..2e3ccd9e4 100644 --- a/src/Cuemon.Data/Cuemon.Data.csproj +++ b/src/Cuemon.Data/Cuemon.Data.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 110bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj b/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj index 3694c6c14..3bcfbebf7 100644 --- a/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj +++ b/src/Cuemon.Diagnostics/Cuemon.Diagnostics.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 1a0bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj b/src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj index d13b93031..ea3ab0dae 100644 --- a/src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Authentication/Cuemon.Extensions.AspNetCore.Authentication.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;netstandard2.0 + net5.0;netcoreapp3.0;netstandard2.0 220bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj index a22fc909a..87db9ccfe 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;netstandard2.0 + net5.0;netcoreapp3.0;netstandard2.0 a60adf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj index 2996389ac..9abe18c18 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;netstandard2.0 + net5.0;netcoreapp3.0;netstandard2.0 a70adf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj b/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj index 7c76b109a..c998d3a12 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Cuemon.Extensions.AspNetCore.Mvc.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0;netstandard2.0 + net5.0;netcoreapp3.0;netstandard2.0 a50adf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj b/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj index f6669960c..2c17a48ae 100644 --- a/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj +++ b/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0;netstandard2.0 + net5.0;netcoreapp3.0;netstandard2.0 a40adf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj b/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj index 00957f8d4..d80c4c7ea 100644 --- a/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj +++ b/src/Cuemon.Extensions.Collections.Generic/Cuemon.Extensions.Collections.Generic.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net5.0;netstandard2.0 190bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj b/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj index 0d2a06aea..c83ad2017 100644 --- a/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj +++ b/src/Cuemon.Extensions.Collections.Specialized/Cuemon.Extensions.Collections.Specialized.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 010bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj b/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj index 7ca7419e7..1331aec56 100644 --- a/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj +++ b/src/Cuemon.Extensions.Core/Cuemon.Extensions.Core.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 020bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj b/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj index eb8ace305..4d17509fc 100644 --- a/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj +++ b/src/Cuemon.Extensions.Data.Integrity/Cuemon.Extensions.Data.Integrity.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 050bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj b/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj index 12ab67377..e39dc4ce0 100644 --- a/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj +++ b/src/Cuemon.Extensions.Data/Cuemon.Extensions.Data.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net5.0;netstandard2.0 100bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj b/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj index 4f3f822ce..8c18dcfef 100644 --- a/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj +++ b/src/Cuemon.Extensions.DependencyInjection/Cuemon.Extensions.DependencyInjection.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 040bdf91-e7c7-4cb4-a39d-e1a5374c5602 @@ -12,9 +12,14 @@ extension-methods extensions add tryadd - - - + + + + + + + + diff --git a/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj b/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj index edf663633..dc46f5f81 100644 --- a/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj +++ b/src/Cuemon.Extensions.Diagnostics/Cuemon.Extensions.Diagnostics.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net5.0;netstandard2.0 0f0bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj b/src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj index d1359f28f..20b41f1d9 100644 --- a/src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj +++ b/src/Cuemon.Extensions.Hosting/Cuemon.Extensions.Hosting.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;netstandard2.0 + net5.0;netcoreapp3.0;netstandard2.0 1d0bdf91-e7c7-4cb4-a39d-e1a5374c5602 @@ -12,12 +12,16 @@ extension-methods extensions local-development non-production host hosting - - + + - + + + + + diff --git a/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj b/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj index 60c5f1532..fe7b399df 100644 --- a/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj +++ b/src/Cuemon.Extensions.IO/Cuemon.Extensions.IO.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + net5.0;netstandard2.1;netstandard2.0 060bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj b/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj index 183916ac0..d64eb54b9 100644 --- a/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj +++ b/src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 070bdf91-e7c7-4cb4-a39d-e1a5374c5602 @@ -12,8 +12,12 @@ extension-methods extensions to-signed-uri validate-signed-uri http-manager-factory slim-http-client-factory i-http-client-factory - - + + + + + + diff --git a/src/Cuemon.Extensions.Net/Http/UriExtensions.cs b/src/Cuemon.Extensions.Net/Http/UriExtensions.cs index 91170f91b..86efc14c7 100644 --- a/src/Cuemon.Extensions.Net/Http/UriExtensions.cs +++ b/src/Cuemon.Extensions.Net/Http/UriExtensions.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Cuemon.Net.Http; +using HttpRequestOptions = Cuemon.Net.Http.HttpRequestOptions; namespace Cuemon.Extensions.Net.Http { diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj index 4915b03d9..0f6938ea2 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Cuemon.Extensions.Newtonsoft.Json.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 080bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj b/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj index 510b19d3a..258d4321b 100644 --- a/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj +++ b/src/Cuemon.Extensions.Reflection/Cuemon.Extensions.Reflection.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 090bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj b/src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj index e2657efeb..1524e8f9e 100644 --- a/src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj +++ b/src/Cuemon.Extensions.Runtime.Caching/Cuemon.Extensions.Runtime.Caching.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net5.0;netstandard2.0 1f0bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj b/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj index 2428af84d..3116acceb 100644 --- a/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj +++ b/src/Cuemon.Extensions.Text/Cuemon.Extensions.Text.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 0a0bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj b/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj index 4259a0cea..772b88e60 100644 --- a/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj +++ b/src/Cuemon.Extensions.Threading/Cuemon.Extensions.Threading.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net5.0;netstandard2.0 180bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj index b637bc475..953ccd66b 100644 --- a/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj +++ b/src/Cuemon.Extensions.Xml/Cuemon.Extensions.Xml.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 0c0bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj index f5e76ce3f..b2e2d9eda 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.csproj @@ -1,6 +1,6 @@ - netcoreapp3.1 + net5.0;netcoreapp3.1 210bdf91-e7c7-4cb4-a39d-e1a5374c5602 @@ -11,8 +11,12 @@ i-mvc-filter-test mvc-filter-test-factory microsoft dependency injection host configuration hosting-environment service-provider configure-services test-server - - + + + + + + diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj index 70a403017..4f51f9ab0 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Cuemon.Extensions.Xunit.Hosting.AspNetCore.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0;netcoreapp3.1 200bdf91-e7c7-4cb4-a39d-e1a5374c5602 @@ -12,8 +12,12 @@ asp-net-core-host-test class-fixture asp-net-core-host-fixture middleware-test-factory microsoft dependency injection host configuration hosting-environment service-provider configure-services test-server - - + + + + + + diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj index 7db061918..eefae00cc 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj +++ b/src/Cuemon.Extensions.Xunit.Hosting/Cuemon.Extensions.Xunit.Hosting.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netcoreapp3.0 + net5.0;netcoreapp3.0;netstandard2.0 1e0bdf91-e7c7-4cb4-a39d-e1a5374c5602 @@ -12,12 +12,20 @@ host-test class-fixture host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services + + + + + + + + - - - - - + + + + + diff --git a/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj b/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj index 000ded379..e368cfa51 100644 --- a/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj +++ b/src/Cuemon.Extensions.Xunit/Cuemon.Extensions.Xunit.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 0d0bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.IO/Cuemon.IO.csproj b/src/Cuemon.IO/Cuemon.IO.csproj index deeea308d..86bdb4e78 100644 --- a/src/Cuemon.IO/Cuemon.IO.csproj +++ b/src/Cuemon.IO/Cuemon.IO.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + net5.0;netstandard2.1;netstandard2.0 170bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs b/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs index aa3611072..0abdff209 100644 --- a/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs +++ b/src/Cuemon.IO/Extensions/StreamDecoratorExtensions.cs @@ -202,7 +202,7 @@ private static async Task ToEncodedStringAsyncCore(this IDecorator /// Compress the enclosed of the specified using the Brotli algorithm. /// diff --git a/src/Cuemon.Net/Cuemon.Net.csproj b/src/Cuemon.Net/Cuemon.Net.csproj index 7087d3c71..aa32713f2 100644 --- a/src/Cuemon.Net/Cuemon.Net.csproj +++ b/src/Cuemon.Net/Cuemon.Net.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net5.0;netstandard2.0 140bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Resilience/Cuemon.Resilience.csproj b/src/Cuemon.Resilience/Cuemon.Resilience.csproj index 95798dae1..9e6c9730a 100644 --- a/src/Cuemon.Resilience/Cuemon.Resilience.csproj +++ b/src/Cuemon.Resilience/Cuemon.Resilience.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net5.0;netstandard2.0 0e0bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj b/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj index 81c0308a7..e984a64ab 100644 --- a/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj +++ b/src/Cuemon.Runtime.Caching/Cuemon.Runtime.Caching.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0;netstandard2.0 160bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj b/src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj index a76121b47..4103ed5ed 100644 --- a/src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj +++ b/src/Cuemon.Security.Cryptography/Cuemon.Security.Cryptography.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net5.0;netstandard2.0 1b0bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Threading/Cuemon.Threading.csproj b/src/Cuemon.Threading/Cuemon.Threading.csproj index 95ebed6a5..ab18ddbf3 100644 --- a/src/Cuemon.Threading/Cuemon.Threading.csproj +++ b/src/Cuemon.Threading/Cuemon.Threading.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net5.0;netstandard2.0 150bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Cuemon.Xml/Cuemon.Xml.csproj b/src/Cuemon.Xml/Cuemon.Xml.csproj index 54e699972..fd66a4877 100644 --- a/src/Cuemon.Xml/Cuemon.Xml.csproj +++ b/src/Cuemon.Xml/Cuemon.Xml.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net5.0;netstandard2.0 120bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj b/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj index ce585ddc6..b3553399c 100644 --- a/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj +++ b/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/test/Cuemon.Extensions.Xunit.Tests/Cuemon.Extensions.Xunit.Tests.csproj b/test/Cuemon.Extensions.Xunit.Tests/Cuemon.Extensions.Xunit.Tests.csproj index b5290d009..5960bf3ea 100644 --- a/test/Cuemon.Extensions.Xunit.Tests/Cuemon.Extensions.Xunit.Tests.csproj +++ b/test/Cuemon.Extensions.Xunit.Tests/Cuemon.Extensions.Xunit.Tests.csproj @@ -4,8 +4,4 @@ Cuemon.Extensions.Xunit - - - - \ No newline at end of file From b021cc794324a2a03392fa7f0ec7f25f3c426a70 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 23 Feb 2021 00:44:06 +0100 Subject: [PATCH 366/385] DocFX update. --- Dockerfile.docfx | 2 +- docfx/docfx.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.docfx b/Dockerfile.docfx index b48d35686..a9a550192 100644 --- a/Dockerfile.docfx +++ b/Dockerfile.docfx @@ -4,7 +4,7 @@ FROM nginx:1.19.2 AS base RUN rm -rf /usr/share/nginx/html/* FROM mono:6.10.0.104 AS build -ARG DOCFX_VERSION=v2.56.2 +ARG DOCFX_VERSION=v2.56.6 ENV PATH ${PATH}:/opt/docfx ENV DOCFX_SOURCE_BRANCH_NAME="development" diff --git a/docfx/docfx.json b/docfx/docfx.json index 01a3eb0b0..d1006e714 100644 --- a/docfx/docfx.json +++ b/docfx/docfx.json @@ -121,7 +121,7 @@ ], "globalMetadata": { "_appTitle": "Cuemon for .NET", - "_appFooter": "Copyright 2008-2020 Geekle. All rights reserved. Code with passion; love your code; deliver with pride. 👨‍💻️🔥❤️🚀🤘
Generated by DocFX
", + "_appFooter": "Copyright 2008-2021 Geekle. All rights reserved. Code with passion; love your code; deliver with pride. 👨‍💻️🔥❤️🚀🤘
Generated by DocFX
", "_appLogoPath": "images/50x50.png", "_appFaviconPath": "images/favicon.ico", "_enableSearch": false, From 8270147bd8ce1c4af00b15ccde3eee2fa0f026e2 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 23 Feb 2021 01:12:41 +0100 Subject: [PATCH 367/385] Fix to NET5 ListSeperator; https://github.com/dotnet/runtime/issues/43795 --- Directory.Build.props | 2 +- test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs | 2 +- .../Cuemon.Data.SqlClient.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 96adb4919..cfb2628d2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -47,7 +47,7 @@ - netcoreapp3.1 + net5.0 false false diff --git a/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs b/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs index c1bd35541..9e694d3d9 100644 --- a/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs +++ b/test/Cuemon.Core.Tests/TypeDecoratorExtensionsTest.cs @@ -261,7 +261,7 @@ public void ToFriendlyName_ShouldProvideDefaultImplementationOfTypes() Assert.Equal("Tuple", noGenericsString); if (Environment.OSVersion.Platform == PlatformID.Win32NT) { - var seCultureInfo = Decorator.Enclose(typeof(Tuple)).ToFriendlyName(o => o.FormatProvider = CultureInfo.GetCultureInfo("se-SV")); // unix has different culture interpretation + var seCultureInfo = Decorator.Enclose(typeof(Tuple)).ToFriendlyName(o => o.FormatProvider = CultureInfo.GetCultureInfo("sv-SE")); // unix has different culture interpretation Assert.Equal("Tuple", seCultureInfo); } } diff --git a/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj b/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj index b3553399c..0303a138d 100644 --- a/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj +++ b/test/Cuemon.Data.SqlClient.Tests/Cuemon.Data.SqlClient.Tests.csproj @@ -6,7 +6,7 @@ - + From 078056ff07b46ad8dc52cca96dbfdce4d3a2d354 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Tue, 23 Feb 2021 02:54:40 +0100 Subject: [PATCH 368/385] Changed due to small docker instance on Azure. --- .../ParallelFactoryTest.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs index b49290f3c..7b804d7b7 100644 --- a/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs +++ b/test/Cuemon.Threading.Tests/ParallelFactoryTest.cs @@ -13,9 +13,10 @@ namespace Cuemon.Threading { public class ParallelFactoryTest : Test { + private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); private readonly TimeSpan _maxAllowedTestTime = TimeSpan.FromMinutes(1); - private readonly TimeSpan _longRunningTaskWaitTime = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? TimeSpan.FromMilliseconds(1) : TimeSpan.FromMilliseconds(10); - private readonly int _extremePartitionSize = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 512 : 4096; + private readonly TimeSpan _longRunningTaskWaitTime = IsLinux ? TimeSpan.FromMilliseconds(1) : TimeSpan.FromMilliseconds(10); + private readonly int _extremePartitionSize = IsLinux ? 128 : 4096; public ParallelFactoryTest(ITestOutputHelper output) : base(output) { @@ -106,7 +107,7 @@ public void For_ShouldRunConcurrent_LongRunning_ExtremePartition() }, o => { o.CancellationToken = cts.Token; - o.PartitionSize = _extremePartitionSize; + o.PartitionSize = MaxThreadCount; }); Assert.Equal(count, cb.Count); @@ -199,7 +200,7 @@ public void ForResult_ShouldRunConcurrent_LongRunning_ExtremePartition() }, o => { o.CancellationToken = cts.Token; - o.PartitionSize = _extremePartitionSize; + o.PartitionSize = MaxThreadCount; }); Assert.Equal(count, cb.Count); @@ -292,7 +293,7 @@ public void ForEach_ShouldRunConcurrent_LongRunning_ExtremePartition() }, o => { o.CancellationToken = cts.Token; - o.PartitionSize = _extremePartitionSize; + o.PartitionSize = MaxThreadCount; }); Assert.Equal(count, cb.Count); @@ -389,7 +390,7 @@ public void ForEachResult_ShouldRunConcurrent_LongRunning_ExtremePartition() }, o => { o.CancellationToken = cts.Token; - o.PartitionSize = _extremePartitionSize; + o.PartitionSize = MaxThreadCount; }); Assert.Equal(count, cb.Count); @@ -485,7 +486,7 @@ public void While_ShouldRunConcurrent_LongRunning_ExtremePartition() }, o => { o.CancellationToken = cts.Token; - o.PartitionSize = _extremePartitionSize; + o.PartitionSize = MaxThreadCount; }); Assert.Equal(count, cb.Count); @@ -586,11 +587,13 @@ public void WhileResult_ShouldRunConcurrent_LongRunning_ExtremePartition() }, o => { o.CancellationToken = cts.Token; - o.PartitionSize = _extremePartitionSize; + o.PartitionSize = MaxThreadCount; }); Assert.Equal(count, cb.Count); Assert.True(result.SequenceEqual(cb.OrderBy(i => i)), "result.SequenceEqual(cb.OrderBy(i => i))"); } + + private static int MaxThreadCount => IsLinux ? 1024 * 8 : 1024 * 32; } } \ No newline at end of file From bf837c88f1679e240855a352bb4a6f52be838048 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 24 Feb 2021 01:16:00 +0100 Subject: [PATCH 369/385] Refactored to be consistent with earlier efforts. --- .../Authenticator.cs | 5 - .../Basic/BasicAuthenticationMiddleware.cs | 34 +---- .../Basic/BasicAuthenticationOptions.cs | 18 +++ .../Basic/BasicAuthorizationHeader.cs | 131 ++++++++++++++++++ .../Basic/BasicAuthorizationHeaderBuilder.cs | 48 +++++++ .../Basic/BasicFields.cs | 28 ++++ .../Digest/DigestAuthenticationMiddleware.cs | 2 - .../GlobalSuppressions.cs | 2 +- .../Hmac/HmacAuthorizationHeaderBuilder.cs | 1 - .../BasicAuthenticationMiddlewareTest.cs | 65 ++++++++- 10 files changed, 290 insertions(+), 44 deletions(-) create mode 100644 src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthorizationHeader.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthorizationHeaderBuilder.cs create mode 100644 src/Cuemon.AspNetCore.Authentication/Basic/BasicFields.cs diff --git a/src/Cuemon.AspNetCore.Authentication/Authenticator.cs b/src/Cuemon.AspNetCore.Authentication/Authenticator.cs index f76dda15f..ba5925867 100644 --- a/src/Cuemon.AspNetCore.Authentication/Authenticator.cs +++ b/src/Cuemon.AspNetCore.Authentication/Authenticator.cs @@ -66,10 +66,5 @@ private static bool TryGetPrincipal(HttpContext context, Func { - context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{AuthenticationScheme} realm=\"{Options.Realm}\"")); + context.Response.Headers.Add(HeaderNames.WWWAuthenticate, FormattableString.Invariant($"{BasicAuthorizationHeader.Scheme} realm=\"{Options.Realm}\"")); return Task.CompletedTask; }); response.StatusCode = (int)message.StatusCode; @@ -56,38 +54,16 @@ await Decorator.Enclose(context).InvokeAuthenticationAsync(Options, async (messa await Next(context).ConfigureAwait(false); } - /// - /// Gets the name of the authentication scheme. - /// - /// The name of the authentication scheme. - public string AuthenticationScheme => "Basic"; - - private bool TryAuthenticate(HttpContext context, Template credentials, out ClaimsPrincipal result) + private bool TryAuthenticate(HttpContext context, BasicAuthorizationHeader header, out ClaimsPrincipal result) { if (Options.Authenticator == null) { throw new InvalidOperationException(FormattableString.Invariant($"The {nameof(Options.Authenticator)} cannot be null.")); } - result = Options.Authenticator(credentials.Arg1, credentials.Arg2); + result = Options.Authenticator(header.UserName, header.Password); return Condition.IsNotNull(result); } - private Template AuthorizationHeaderParser(HttpContext context, string authorizationHeader) + private BasicAuthorizationHeader AuthorizationHeaderParser(HttpContext context, string authorizationHeader) { - if (Authenticator.IsAuthenticationSchemeValid(authorizationHeader, AuthenticationScheme)) - { - var base64Credentials = authorizationHeader.Remove(0, AuthenticationScheme.Length + 1); - if (Condition.IsBase64(base64Credentials)) - { - var credentials = Convertible.ToString(Convert.FromBase64String(base64Credentials), options => - { - options.Encoding = Encoding.ASCII; - options.Preamble = PreambleSequence.Remove; - }).Split(':'); - if (credentials.Length == 2 && - !string.IsNullOrEmpty(credentials[0]) && - !string.IsNullOrEmpty(credentials[1])) - { return Template.CreateTwo(credentials[0], credentials[1]); } - } - } - return null; + return BasicAuthorizationHeader.Create(authorizationHeader); } } } \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationOptions.cs b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationOptions.cs index 20dae0b72..16cbc1eef 100644 --- a/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationOptions.cs +++ b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthenticationOptions.cs @@ -9,8 +9,26 @@ public sealed class BasicAuthenticationOptions : AuthenticationOptions /// /// Initializes a new instance of the class. /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// null + /// + /// + /// + /// AuthenticationServer + /// + /// + /// public BasicAuthenticationOptions() { + Realm = "AuthenticationServer"; } /// diff --git a/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthorizationHeader.cs b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthorizationHeader.cs new file mode 100644 index 000000000..63b6ee829 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthorizationHeader.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Cuemon.Text; + +namespace Cuemon.AspNetCore.Authentication.Basic +{ + /// + /// Provides a representation of a HTTP Basic Authentication header. + /// Implements the + /// + /// + public class BasicAuthorizationHeader : AuthorizationHeader + { + /// + /// Creates an instance of from the specified parameters. + /// + /// The raw HTTP authorization header. + /// An instance of . + /// + /// cannot be null. + /// + /// + /// cannot be empty or consist only of white-space characters. + /// + public static BasicAuthorizationHeader Create(string authorizationHeader) + { + Validator.ThrowIfNullOrWhitespace(authorizationHeader, nameof(authorizationHeader)); + return new BasicAuthorizationHeader().Parse(authorizationHeader, null) as BasicAuthorizationHeader; + } + + /// + /// The default authentication scheme of the . + /// + public const string Scheme = "Basic"; + + BasicAuthorizationHeader() : base(Scheme) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The username of the credentials. + /// The password of the credentials. + /// + /// cannot be null -or- + /// cannot be null. + /// + /// + /// cannot be empty or consist only of white-space characters -or- + /// cannot be empty or consist only of white-space characters -or- + /// does not allow the presence of the colon character. + /// + public BasicAuthorizationHeader(string username, string password) : base(Scheme) + { + Validator.ThrowIfNullOrWhitespace(username, nameof(username)); + Validator.ThrowIfNullOrWhitespace(password, nameof(password)); + Validator.ThrowIfTrue(() => username.Contains(":"), nameof(username), $"Colon is not allowed as part of the {nameof(username)}."); + UserName = username; + Password = password; + } + + + /// + /// Gets the username of the credentials. + /// + /// The username of the credentials. + public string UserName { get; } + + /// + /// Gets the password of the credentials. + /// + /// The password of the credentials. + public string Password { get; } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + var credentials = Convert.ToBase64String(Decorator.Enclose($"{UserName}:{Password}").ToByteArray()); + return $"{AuthenticationScheme} {credentials}"; + } + + /// + /// Parses the specified . + /// + /// The authorization header to parse. + /// The which need to be configured. + /// An equivalent of . + public override AuthorizationHeader Parse(string authorizationHeader, Action setup) + { + Validator.ThrowIfNullOrWhitespace(authorizationHeader, nameof(authorizationHeader)); + Validator.ThrowIfFalse(() => authorizationHeader.StartsWith(AuthenticationScheme), nameof(authorizationHeader), $"Header did not start with {AuthenticationScheme}."); + + var headerWithoutScheme = authorizationHeader.Remove(0, AuthenticationScheme.Length + 1); + var credentials = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { BasicFields.Credentials, headerWithoutScheme.Trim() } + }; + return ParseCore(credentials); + } + + /// + /// The core parser that resolves an from a set of . + /// + /// The credentials used in authentication. + /// An equivalent of . + protected override AuthorizationHeader ParseCore(IReadOnlyDictionary credentials) + { + if (credentials.TryGetValue(BasicFields.Credentials, out var base64EncodedCredentials) && Condition.IsBase64(base64EncodedCredentials)) + { + var plainCredentials = Convertible.ToString(Convert.FromBase64String(base64EncodedCredentials), options => + { + options.Encoding = Encoding.ASCII; + options.Preamble = PreambleSequence.Remove; + }).Split(new [] { ':' }, 2); + + if (plainCredentials.Length == 2 && + !string.IsNullOrWhiteSpace(plainCredentials[0]) && + !string.IsNullOrWhiteSpace(plainCredentials[1])) + { + return new BasicAuthorizationHeader(plainCredentials[0], plainCredentials[1]); + } + } + return null; + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthorizationHeaderBuilder.cs b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthorizationHeaderBuilder.cs new file mode 100644 index 000000000..8737a4a45 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Basic/BasicAuthorizationHeaderBuilder.cs @@ -0,0 +1,48 @@ +namespace Cuemon.AspNetCore.Authentication.Basic +{ + /// + /// Provides a way to fluently represent a HTTP Basic Authentication header. + /// + public class BasicAuthorizationHeaderBuilder : AuthorizationHeaderBuilder + { + /// + /// Initializes a new instance of the class. + /// + public BasicAuthorizationHeaderBuilder() : base(BasicAuthorizationHeader.Scheme) + { + MapRelation(nameof(AddUserName), BasicFields.UserName); + MapRelation(nameof(AddPassword), BasicFields.Password); + } + + + /// + /// Adds the credential scope that defines the remote resource. + /// + /// The credential scope that defines the remote resource. + /// An that can be used to further build the HTTP HMAC Authentication header. + public BasicAuthorizationHeaderBuilder AddUserName(string username) + { + return AddOrUpdate(BasicFields.UserName, username); + } + + /// + /// Adds the client identifier that is the public key of the signing process. + /// + /// The client identifier that is the public key of the signing process. + /// An that can be used to further build the HTTP HMAC Authentication header. + public BasicAuthorizationHeaderBuilder AddPassword(string password) + { + return AddOrUpdate(BasicFields.Password, password); + } + + /// + /// Builds an instance of that implements . + /// + /// An instance of . + public override BasicAuthorizationHeader Build() + { + ValidateData(BasicFields.UserName, BasicFields.Password); + return new BasicAuthorizationHeader(Data[BasicFields.UserName], Data[BasicFields.Password]); + } + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Basic/BasicFields.cs b/src/Cuemon.AspNetCore.Authentication/Basic/BasicFields.cs new file mode 100644 index 000000000..7824921ac --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Basic/BasicFields.cs @@ -0,0 +1,28 @@ +namespace Cuemon.AspNetCore.Authentication.Basic +{ + /// + /// A collection of constants for . + /// + public static class BasicFields + { + /// + /// The realm field of a HTTP Basic access authentication. + /// + public const string Realm = "realm"; + + /// + /// The username of the . + /// + public const string UserName = "username"; + + /// + /// The password of the . + /// + public const string Password = "password"; + + /// + /// The credentials of the HTTP Basic access authentication. + /// + public const string Credentials = "credentials"; + } +} \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationMiddleware.cs b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationMiddleware.cs index 00a16084e..dcab39d81 100644 --- a/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationMiddleware.cs +++ b/src/Cuemon.AspNetCore.Authentication/Digest/DigestAuthenticationMiddleware.cs @@ -116,8 +116,6 @@ private DigestAuthorizationHeader AuthorizationHeaderParser(HttpContext context, return DigestAuthorizationHeader.Create(authorizationHeader); } - - private static string ParseAlgorithm(UnkeyedCryptoAlgorithm algorithm) { switch (algorithm) diff --git a/src/Cuemon.AspNetCore.Authentication/GlobalSuppressions.cs b/src/Cuemon.AspNetCore.Authentication/GlobalSuppressions.cs index 60f04d4bc..f678d64ee 100644 --- a/src/Cuemon.AspNetCore.Authentication/GlobalSuppressions.cs +++ b/src/Cuemon.AspNetCore.Authentication/GlobalSuppressions.cs @@ -5,4 +5,4 @@ using System.Diagnostics.CodeAnalysis; -[assembly: SuppressMessage("Critical Code Smell", "S927:parameter names should match base declaration and other partial definitions", Justification = "Clarity.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Authentication.DigestAccessAuthenticationMiddleware.InvokeAsync(Microsoft.AspNetCore.Http.HttpContext,Cuemon.AspNetCore.Authentication.INonceTracker)~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "By design to support the Digest protocol.", Scope = "member", Target = "~M:Cuemon.AspNetCore.Authentication.Digest.DigestAuthorizationHeader.#ctor(System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String)")] \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeaderBuilder.cs b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeaderBuilder.cs index 8d4e01e28..2c37d60f1 100644 --- a/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeaderBuilder.cs +++ b/src/Cuemon.AspNetCore.Authentication/Hmac/HmacAuthorizationHeaderBuilder.cs @@ -50,7 +50,6 @@ public HmacAuthorizationHeaderBuilder(string authenticationScheme = HmacAuthoriz /// Adds the credential scope that defines the remote resource. /// /// The credential scope that defines the remote resource. - /// A reference to this instance after the operation has completed. /// An that can be used to further build the HTTP HMAC Authentication header. public HmacAuthorizationHeaderBuilder AddCredentialScope(string credentialScope) { diff --git a/test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs b/test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs index 59dfc5659..a49f659dc 100644 --- a/test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Authentication.Tests/BasicAuthenticationMiddlewareTest.cs @@ -1,3 +1,4 @@ +using System; using System.Security.Claims; using System.Threading.Tasks; using Cuemon.AspNetCore.Authentication.Basic; @@ -21,7 +22,6 @@ public BasicAuthenticationMiddlewareTest(ITestOutputHelper output) : base(output { } - [Fact] public async Task InvokeAsync_ShouldNotBeAuthenticated() { @@ -45,7 +45,53 @@ public async Task InvokeAsync_ShouldNotBeAuthenticated() var context = middleware.ServiceProvider.GetRequiredService().HttpContext; var options = middleware.ServiceProvider.GetRequiredService>(); var pipeline = middleware.Application.Build(); + + var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + + var wwwAuthenticate = context.Response.Headers[HeaderNames.WWWAuthenticate]; + + TestOutput.WriteLine(wwwAuthenticate); + + var bb = new BasicAuthorizationHeaderBuilder() + .AddUserName("Agent") + .AddPassword("Test"); + context.Request.Headers.Add(HeaderNames.Authorization, bb.Build().ToString()); + + ue = await Assert.ThrowsAsync(async () => await pipeline(context)); + + Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + } + } + + [Fact] + public async Task InvokeAsync_ShouldFailBecauseOfColonInUserName() + { + using (var middleware = MiddlewareTestFactory.CreateMiddlewareTest(app => + { + app.UseFakeHttpResponseTrigger(o => o.ShortCircuitOnStarting = true); + app.UseBasicAuthentication(); + }, services => + { + services.Configure(o => + { + o.Authenticator = (username, password) => + { + return null; + }; + o.RequireSecureConnection = false; + }); + services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); + })) + { + var context = middleware.ServiceProvider.GetRequiredService().HttpContext; + var options = middleware.ServiceProvider.GetRequiredService>(); + var pipeline = middleware.Application.Build(); + var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); @@ -55,8 +101,12 @@ public async Task InvokeAsync_ShouldNotBeAuthenticated() TestOutput.WriteLine(wwwAuthenticate); - var encodedUsernameAndPassword = "Agent:Test".ToByteArray().ToBase64String(); - context.Request.Headers.Add(HeaderNames.Authorization, $"Basic {encodedUsernameAndPassword}"); + var bb = new BasicAuthorizationHeaderBuilder() + .AddUserName("Ag:ent") + .AddPassword("Test"); + + var ae = Assert.Throws(() => context.Request.Headers.Add(HeaderNames.Authorization, bb.Build().ToString())); + Assert.Contains("Colon is not allowed as part of the", ae.Message); ue = await Assert.ThrowsAsync(async () => await pipeline(context)); @@ -94,7 +144,7 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeader( var context = middleware.ServiceProvider.GetRequiredService().HttpContext; var options = middleware.ServiceProvider.GetRequiredService>(); var pipeline = middleware.Application.Build(); - + var ue = await Assert.ThrowsAsync(async () => await pipeline(context)); Assert.Equal(ue.Message, options.Value.UnauthorizedMessage); @@ -104,8 +154,11 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeader( TestOutput.WriteLine(wwwAuthenticate); - var encodedUsernameAndPassword = "Agent:Test".ToByteArray().ToBase64String(); - context.Request.Headers.Add(HeaderNames.Authorization, $"Basic {encodedUsernameAndPassword}"); + var bb = new BasicAuthorizationHeaderBuilder() + .AddUserName("Agent") + .AddPassword("Test"); + + context.Request.Headers.Add(HeaderNames.Authorization, bb.Build().ToString()); await pipeline(context); From 1bd35b2a914a9dc3c4c824895663774f5d1f8b7c Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 26 Feb 2021 00:08:35 +0100 Subject: [PATCH 370/385] Updated DocFx files. --- .../Cuemon.AspNetCore.Authentication.Basic.md | 16 ++++++++++ ...Cuemon.AspNetCore.Authentication.Digest.md | 16 ++++++++++ .../Cuemon.AspNetCore.Authentication.Hmac.md | 16 ++++++++++ .../Cuemon.AspNetCore.Authentication.md | 15 ++++++++- .../namespaces/Cuemon.AspNetCore.Builder.md | 2 +- .../Cuemon.AspNetCore.Configuration.md | 2 +- .../Cuemon.AspNetCore.Diagnostics.md | 2 +- .../namespaces/Cuemon.AspNetCore.Hosting.md | 2 +- .../Cuemon.AspNetCore.Http.Headers.md | 2 +- .../Cuemon.AspNetCore.Http.Throttling.md | 2 +- .../api/namespaces/Cuemon.AspNetCore.Http.md | 2 +- ...Cuemon.AspNetCore.Mvc.Filters.Cacheable.md | 2 +- ...emon.AspNetCore.Mvc.Filters.Diagnostics.md | 2 +- .../Cuemon.AspNetCore.Mvc.Filters.Headers.md | 2 +- ...mon.AspNetCore.Mvc.Filters.ModelBinding.md | 2 +- ...uemon.AspNetCore.Mvc.Filters.Throttling.md | 2 +- .../Cuemon.AspNetCore.Mvc.Filters.md | 2 +- docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md | 2 +- .../Cuemon.AspNetCore.Razor.TagHelpers.md | 15 ++++++++- .../api/namespaces/Cuemon.AspNetCore.Razor.md | 5 --- docfx/api/namespaces/Cuemon.AspNetCore.md | 2 +- .../namespaces/Cuemon.Collections.Generic.md | 2 +- docfx/api/namespaces/Cuemon.Collections.md | 2 +- docfx/api/namespaces/Cuemon.Configuration.md | 2 +- docfx/api/namespaces/Cuemon.Data.Integrity.md | 2 +- docfx/api/namespaces/Cuemon.Data.SqlClient.md | 2 +- docfx/api/namespaces/Cuemon.Data.Xml.md | 13 +++++++- docfx/api/namespaces/Cuemon.Data.md | 2 +- docfx/api/namespaces/Cuemon.Diagnostics.md | 2 +- .../Cuemon.Extensions.AspNetCore.Builder.md | 2 +- ...on.Extensions.AspNetCore.Data.Integrity.md | 2 +- ...n.Extensions.AspNetCore.Http.Throttling.md | 2 +- .../Cuemon.Extensions.AspNetCore.Http.md | 2 +- ...Extensions.AspNetCore.Mvc.Configuration.md | 2 +- ...nsions.AspNetCore.Mvc.Filters.Cacheable.md | 2 +- ...ions.AspNetCore.Mvc.Filters.Diagnostics.md | 2 +- ...c.Formatters.Newtonsoft.Json.Converters.md | 2 +- ...pNetCore.Mvc.Formatters.Newtonsoft.Json.md | 2 +- ...spNetCore.Mvc.Formatters.Xml.Converters.md | 2 +- ...xtensions.AspNetCore.Mvc.Formatters.Xml.md | 2 +- ...mon.Extensions.AspNetCore.Mvc.Rendering.md | 2 +- .../Cuemon.Extensions.AspNetCore.Mvc.md | 2 +- .../Cuemon.Extensions.AspNetCore.md | 2 +- .../Cuemon.Extensions.Collections.Generic.md | 2 +- ...emon.Extensions.Collections.Specialized.md | 2 +- .../Cuemon.Extensions.Data.Integrity.md | 2 +- .../api/namespaces/Cuemon.Extensions.Data.md | 2 +- .../Cuemon.Extensions.DependencyInjection.md | 2 +- .../Cuemon.Extensions.Diagnostics.md | 2 +- .../namespaces/Cuemon.Extensions.Hosting.md | 2 +- docfx/api/namespaces/Cuemon.Extensions.IO.md | 2 +- .../namespaces/Cuemon.Extensions.Net.Http.md | 2 +- .../Cuemon.Extensions.Net.Security.md | 2 +- docfx/api/namespaces/Cuemon.Extensions.Net.md | 2 +- ...n.Extensions.Newtonsoft.Json.Converters.md | 2 +- ....Extensions.Newtonsoft.Json.Diagnostics.md | 2 +- ...n.Extensions.Newtonsoft.Json.Formatters.md | 2 +- .../Cuemon.Extensions.Newtonsoft.Json.md | 2 +- .../Cuemon.Extensions.Reflection.md | 2 +- .../Cuemon.Extensions.Runtime.Caching.md | 2 +- .../api/namespaces/Cuemon.Extensions.Text.md | 2 +- .../Cuemon.Extensions.Threading.Tasks.md | 2 +- .../namespaces/Cuemon.Extensions.Threading.md | 2 +- .../namespaces/Cuemon.Extensions.Xml.Linq.md | 2 +- ...Extensions.Xml.Serialization.Converters.md | 2 +- ...xtensions.Xml.Serialization.Diagnostics.md | 2 +- .../Cuemon.Extensions.Xml.Serialization.md | 2 +- docfx/api/namespaces/Cuemon.Extensions.Xml.md | 2 +- ...Extensions.Xunit.Hosting.AspNetCore.Mvc.md | 2 +- ...mon.Extensions.Xunit.Hosting.AspNetCore.md | 2 +- .../Cuemon.Extensions.Xunit.Hosting.md | 2 +- .../api/namespaces/Cuemon.Extensions.Xunit.md | 2 +- docfx/api/namespaces/Cuemon.Extensions.md | 2 +- docfx/api/namespaces/Cuemon.Globalization.md | 2 +- docfx/api/namespaces/Cuemon.IO.md | 2 +- docfx/api/namespaces/Cuemon.Messaging.md | 2 +- docfx/api/namespaces/Cuemon.Net.Http.md | 2 +- docfx/api/namespaces/Cuemon.Net.Mail.md | 2 +- docfx/api/namespaces/Cuemon.Net.md | 2 +- docfx/api/namespaces/Cuemon.Reflection.md | 2 +- docfx/api/namespaces/Cuemon.Resilience.md | 2 +- .../api/namespaces/Cuemon.Runtime.Caching.md | 2 +- ...Cuemon.Runtime.Serialization.Formatters.md | 2 +- .../Cuemon.Runtime.Serialization.md | 2 +- docfx/api/namespaces/Cuemon.Runtime.md | 2 +- .../Cuemon.Security.Cryptography.md | 2 +- docfx/api/namespaces/Cuemon.Security.md | 2 +- docfx/api/namespaces/Cuemon.Text.md | 2 +- docfx/api/namespaces/Cuemon.Threading.md | 2 +- docfx/api/namespaces/Cuemon.Xml.Linq.md | 2 +- .../Cuemon.Xml.Serialization.Converters.md | 2 +- .../Cuemon.Xml.Serialization.Formatters.md | 2 +- .../namespaces/Cuemon.Xml.Serialization.md | 2 +- docfx/api/namespaces/Cuemon.Xml.XPath.md | 2 +- docfx/api/namespaces/Cuemon.Xml.md | 2 +- docfx/api/namespaces/Cuemon.md | 2 +- docfx/index.md | 31 ------------------- docfx/templates/cuemon/index.html.tmpl | 19 ------------ .../cuemon/partials/logo.tmpl.partial | 5 +++ 99 files changed, 182 insertions(+), 147 deletions(-) create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Basic.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Digest.md create mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Hmac.md delete mode 100644 docfx/api/namespaces/Cuemon.AspNetCore.Razor.md delete mode 100644 docfx/index.md delete mode 100644 docfx/templates/cuemon/index.html.tmpl create mode 100644 docfx/templates/cuemon/partials/logo.tmpl.partial diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Basic.md b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Basic.md new file mode 100644 index 000000000..7ffa1f5a7 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Basic.md @@ -0,0 +1,16 @@ +--- +uid: Cuemon.AspNetCore.Authentication.Basic +summary: *content +--- +The Cuemon.AspNetCore.Authentication.Basic namespace contains types that enable support for Basic Authentication Scheme. + +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Authentication)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Authentication)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Authentication) + +NuGet packages 📦\ +[Cuemon.AspNetCore.Authentication (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Authentication)\ +[Cuemon.AspNetCore.Authentication (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Authentication) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Digest.md b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Digest.md new file mode 100644 index 000000000..8cb1bec78 --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Digest.md @@ -0,0 +1,16 @@ +--- +uid: Cuemon.AspNetCore.Authentication.Digest +summary: *content +--- +The Cuemon.AspNetCore.Authentication.Digest namespace contains types that enable support for Digest Access Authentication Scheme. + +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Authentication)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Authentication)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Authentication) + +NuGet packages 📦\ +[Cuemon.AspNetCore.Authentication (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Authentication)\ +[Cuemon.AspNetCore.Authentication (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Authentication) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Hmac.md b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Hmac.md new file mode 100644 index 000000000..93b0c715d --- /dev/null +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Hmac.md @@ -0,0 +1,16 @@ +--- +uid: Cuemon.AspNetCore.Authentication.Hmac +summary: *content +--- +The Cuemon.AspNetCore.Authentication.Hmac namespace contains types that enable support for HMAC Access Authentication Scheme. Inspired by AWS Signature Version 4 [Authenticating Requests: Using the Authorization Header](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html) and [Signing AWS requests with Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html). + +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Authentication)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Authentication)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Authentication) + +NuGet packages 📦\ +[Cuemon.AspNetCore.Authentication (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Authentication)\ +[Cuemon.AspNetCore.Authentication (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Authentication) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.md b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.md index b99877655..8c85671b9 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore.Authentication summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Authentication namespace contains types that enable support for authentication using the concept of an Authenticator, AuthorizationHeader and (to tie the knots) an AuthorizationHeaderBuilder. Basic-, Digest Access- and HMAC Authentication is provided out-of-the-box. The namespace is an addition to the Microsoft.AspNetCore.Authentication namespace. + +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 + +Complements: [Microsoft.AspNetCore.Authentication namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication?view=aspnetcore-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Authentication)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Authentication)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Authentication) + +NuGet packages 📦\ +[Cuemon.AspNetCore.Authentication (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Authentication)\ +[Cuemon.AspNetCore.Authentication (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Authentication) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Builder.md b/docfx/api/namespaces/Cuemon.AspNetCore.Builder.md index 3f70a4936..091b508ee 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Builder.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Builder.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Builder namespace contains types that supports adding either middleware or configurable middleware types to the application request pipeline. The namespace is an addition to the Microsoft.AspNetCore.Builder namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Related: [Cuemon.Extensions.AspNetCore.Builder namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Builder.html) 📘 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md b/docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md index 4254f53ae..b6b0f457a 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Configuration.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Configuration namespace contains types that provides a way to support a [cache busting strategy](https://www.keycdn.com/support/what-is-cache-busting). -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Related: [Cuemon.Extensions.AspNetCore.Mvc.Configuration namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Mvc.Configuration.html) 📘 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Diagnostics.md b/docfx/api/namespaces/Cuemon.AspNetCore.Diagnostics.md index 3021d324a..f475cbae3 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Diagnostics.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Diagnostics namespace contains types that provides a way to support the Server-Timing header for communicating metrics about the request-response cycle to an user agent. The namespace is an addition to the Microsoft.AspNetCore.Diagnostics namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Diagnostics namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.diagnostics?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md b/docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md index 3a848655b..758f0417d 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Hosting.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Hosting namespace contains types that provides middleware for determining the hosting environment. The namespace is an addition to the Microsoft.AspNetCore.Hosting namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Hosting namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.hosting?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md index f28eb3ba8..0ba680c00 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Headers.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Http.Headers namespace contains types that provides a set of middleware components tied to HTTP headers. The namespace is an addition to the Microsoft.AspNetCore.Http.Headers namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Http.Headers namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.headers?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md index f66e9f4d0..80253f3c3 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Http.Throttling.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Http.Throttling namespace contains types that provides a middleware based throttling mechanism by specifying allowed quota and window duration of HTTP requests tied to a custom context (eg. IP-address, Authorization header, etc.). -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Related: [Cuemon.Extensions.AspNetCore.Http.Throttling namespace](https://docs.cuemon.net/api/aspnet/ext/Cuemon.Extensions.AspNetCore.Http.Throttling.html) 📘 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Http.md b/docfx/api/namespaces/Cuemon.AspNetCore.Http.md index 57f397912..cafd6a7b8 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Http.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Http.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Http namespace contains types focusing on ways to provide developer friendly exception messages optimized for open- and otherwise public application programming interfaces (API). The namespace is an addition to the Microsoft.AspNetCore.Http namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Http namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md index 72b4176d8..1a2d8047e 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Cacheable.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace contains types that specializes in cache expiration and validation models. The namespace is an addition to the Microsoft.AspNetCore.Mvc.Filters namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Mvc.Filters namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md index 4ed78307e..f2540d5a1 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Mvc.Diagnostics namespace contains types that provide ways for developers to describe exceptions including evidence to why an operation faulted optimized for open- and otherwise public application programming interfaces (API). The namespace is an addition to the Microsoft.AspNetCore.Mvc.Filters namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Mvc.Filters namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md index 3f29ae7e6..2d78e4fef 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Headers.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Mvc.Headers namespace contains types that provide filters explicitly written to different types of HTTP headers. The namespace is an addition to the Microsoft.AspNetCore.Mvc.Filters namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Mvc.Filters namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md index dc93f718d..afa9abefd 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.ModelBinding.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Mvc.ModelBinding namespace contains types that alters the built-in way of doing model binding. The namespace is an addition to the Microsoft.AspNetCore.Mvc.ModelBinding namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Mvc.ModelBinding namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.modelbinding?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md index 4a18bd8c0..4555e14cf 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.Throttling.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Mvc.Filters.Throttling namespace contains types that provides filter based throttling mechanism by specifying allowed quota and window duration of HTTP requests tied to a custom context (eg. IP-address, Authorization header, etc.). The namespace is an addition to the Microsoft.AspNetCore.Mvc.Filters namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Mvc.Filters namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md index 81945d633..d7da0dd25 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.Filters.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore.Mvc.Filters namespace contains types that supports a generic way of working with built-in interfaces providing ready-to-use class abstractions. The namespace is an addition to the Microsoft.AspNetCore.Mvc.Filters namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Mvc.Filters namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md index 92921315c..90957ba0c 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Mvc.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore,Mvc namespace contains types that specializes in cache expiration and validation models and an abundant range of ready-to-use filters in the ASP.NET Core MVC pipeline. The namespace is an addition to the Microsoft.AspNetCore.Mvc namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Mvc namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Razor.TagHelpers.md b/docfx/api/namespaces/Cuemon.AspNetCore.Razor.TagHelpers.md index bf09c1294..9da759522 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Razor.TagHelpers.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Razor.TagHelpers.md @@ -2,4 +2,17 @@ uid: Cuemon.AspNetCore.Razor.TagHelpers summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.AspNetCore.Razor.TagHelpers namespace contains types tailored tag helper implementations. The namespace is an addition to the Microsoft.AspNetCore.Razor.TagHelpers namespace. + +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 + +Complements: [Microsoft.AspNetCore.Razor.TagHelpers namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.razor.taghelpers?view=aspnetcore-2.0) 🔗 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Razor.TagHelpers)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Razor.TagHelpers)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Razor.TagHelpers) + +NuGet packages 📦\ +[Cuemon.AspNetCore.Razor.TagHelpers (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Razor.TagHelpers)\ +[Cuemon.AspNetCore.Razor.TagHelpers (Stable and Preview)](https://www.nuget.org/packages/Cuemon.AspNetCore.Razor.TagHelpers) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Razor.md b/docfx/api/namespaces/Cuemon.AspNetCore.Razor.md deleted file mode 100644 index 16fd9d1b2..000000000 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Razor.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -uid: Cuemon.AspNetCore.Razor -summary: *content ---- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.md b/docfx/api/namespaces/Cuemon.AspNetCore.md index abfdb80f6..ce34245bd 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.AspNetCore namespace contains types focusing on providing means for easier plumber coding in the ASP.NET Core pipeline while serving some concrete implementation of the shell as well. The namespace is an addition to the Microsoft.AspNetCore namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore?view=aspnetcore-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Collections.Generic.md b/docfx/api/namespaces/Cuemon.Collections.Generic.md index 5b56675e0..53a1b8fd2 100644 --- a/docfx/api/namespaces/Cuemon.Collections.Generic.md +++ b/docfx/api/namespaces/Cuemon.Collections.Generic.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Collections.Generic namespace contains types that define generic collections that support paging, partitioning, dynamic comparers and some specialized collections such as a read-only enum dictionary and a generic, conditional collection. The namespace is an addition to the System.Collections.Generic namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Collections.Generic namespace](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Collections.md b/docfx/api/namespaces/Cuemon.Collections.md index de1a07ba5..80446ab84 100644 --- a/docfx/api/namespaces/Cuemon.Collections.md +++ b/docfx/api/namespaces/Cuemon.Collections.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Collections namespace contains types that define various collections of objects. The namespace is an addition to the System.Collections namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Collections namespace](https://docs.microsoft.com/en-us/dotnet/api/system.collections?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Configuration.md b/docfx/api/namespaces/Cuemon.Configuration.md index 7b9ab43a0..4e90c033c 100644 --- a/docfx/api/namespaces/Cuemon.Configuration.md +++ b/docfx/api/namespaces/Cuemon.Configuration.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Configuration namespace contains types focusing on writing configurable classes to help suport adhering to Separation of Concerns (SoC). -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Core/Configuration)\ diff --git a/docfx/api/namespaces/Cuemon.Data.Integrity.md b/docfx/api/namespaces/Cuemon.Data.Integrity.md index ce6305ca2..467c6acb3 100644 --- a/docfx/api/namespaces/Cuemon.Data.Integrity.md +++ b/docfx/api/namespaces/Cuemon.Data.Integrity.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Data.Integrity namespace contains types that provide ways for developers to determine and maintain integrity of data that is normally associated with an entity/resource. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Related: [Cuemon.Extensions.Data.Integrity namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Data.Integrity.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Data.SqlClient.md b/docfx/api/namespaces/Cuemon.Data.SqlClient.md index 9435bf3d8..cb9219e5e 100644 --- a/docfx/api/namespaces/Cuemon.Data.SqlClient.md +++ b/docfx/api/namespaces/Cuemon.Data.SqlClient.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Data.SqlClient namespace contains types that provide ways for developers to work with Microsoft SQL Server integrations. The namespace is an addition to the System.Data.SqlClient namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Data.SqlClient namespace](https://docs.microsoft.com/en-us/dotnet/api/system.Data.SqlClient?view=netframework-4.8) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Data.Xml.md b/docfx/api/namespaces/Cuemon.Data.Xml.md index 5b8a93a95..ed7166a59 100644 --- a/docfx/api/namespaces/Cuemon.Data.Xml.md +++ b/docfx/api/namespaces/Cuemon.Data.Xml.md @@ -2,4 +2,15 @@ uid: Cuemon.Data.Xml summary: *content --- -The Cuemon.Collections namespace contains fundamental factories, classes and base classes that define invaluable value and reference types that greatly extends the System namespace. Abundant support for delegates and functional programming. \ No newline at end of file +The Cuemon.Data.Xml namespace contains an implementation of the DataReader class that provides a way of reading a forward-only stream of rows from an XML based data source. + +Availability: NET Standard 2.0, .NET 5.0 + +Github branches 🌱\ +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Data/Xml)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.Data/Xml)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.Data/Xml) + +NuGet packages 📦\ +[Cuemon.Data.SqlClient (CI)](https://nuget.cuemon.net/packages/Cuemon.Data)\ +[Cuemon.Data.SqlClient (Stable and Preview)](https://www.nuget.org/packages/Cuemon.Data) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Data.md b/docfx/api/namespaces/Cuemon.Data.md index fe7543ec0..14fb5a086 100644 --- a/docfx/api/namespaces/Cuemon.Data.md +++ b/docfx/api/namespaces/Cuemon.Data.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Data namespace contains types that provide ways to connect, build and manipulate different data sources. The namespace is an addition to the System.Data namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Data namespace](https://docs.microsoft.com/en-us/dotnet/api/system.Data?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Diagnostics.md b/docfx/api/namespaces/Cuemon.Diagnostics.md index 33a65faa0..bcc81fe8a 100644 --- a/docfx/api/namespaces/Cuemon.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.Diagnostics.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Diagnostics namespace contains types that provide ways for developers to describe exceptions including evidence to why an operation faulted. Also includes a flexible, generic and lambda friendly way to perform both synchronous and asynchronous time measuring operations. The namespace is an addition to the System.Diagnostics namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Diagnostics namespace](https://docs.microsoft.com/en-us/dotnet/api/system.Diagnostics?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md index efd68b556..a07aca809 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Builder.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Builder namespace contains extension methods that complements the Cuemon.AspNetCore.Builder namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.AspNetCore.Builder namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Builder.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md index 5499ad3d6..d00b22675 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Data.Integrity.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Data.Integrity namespace contains extension methods that complements the Cuemon.Data.Integrity namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.Data.Integrity namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Data.Integrity.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md index 3958e88fb..79dce2238 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.Throttling.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Http.Throttling namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Http.Throttling namespace. Provides an in-memory implementation of a throttling cache for ASP.NET Core. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.AspNetCore.Http.Throttling namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Http.Throttling.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md index c88351fc3..3aaa10035 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Http.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Http namespace contains extension methods that complements the Cuemon.AspNetCore.Http namespace while being an addition to the Microsoft.AspNetCore.Http namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.AspNetCore.Http namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Http.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md index dfc96fcf1..bb5ae5edf 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Configuration.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Mvc.Configuration namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Configuration namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides a set of different cache busting strategies for ASP.NET Core MVC that can be easily customized. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.AspNetCore.Configuration namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Configuration.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md index 99bc2fd00..920b9a385 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Mvc.Filters.Cacheable namespace contains extension methods that complements the Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Mvc.Filters.Cacheable.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md index c7fff0eb3..053b1b75b 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Mvc.Filters.Diagnostics namespace contains extension methods that complements the Cuemon.AspNetCore.Mvc.Filters.Diagnostics namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.AspNetCore.Mvc.Filters.Diagnostics namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Mvc.Filters.Diagnostics.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md index c22eb1b05..aae9bf089 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters namespace contains extension methods that complements the Cuemon.Extensions.Newtonsoft.Json.Converters namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.Extensions.Newtonsoft.Json.Converters namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Newtonsoft.Json.Converters.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md index b959a0daa..d6eb73be4 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json namespace contains both types and extension methods that complements the Cuemon.Extensions.Newtonsoft.Json namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides JSON formatters for ASP.NET Core that is powered by Newtonsoft.Json. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.Extensions.Newtonsoft.Json namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Newtonsoft.Json.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md index a3934ed00..e1b7993fe 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.Converters namespace contains extension methods that complements the Cuemon.Extensions.Xml.Serialization.Converters namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.Extensions.Xml.Serialization.Converters namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Xml.Serialization.Converters.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md index cd64f596c..5690aef9b 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml namespace contains both types and extension methods that complements the Cuemon.Extensions.Xml namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides XML formatters for ASP.NET Core that offers same flexibility as the one provided by the JSON equivalent from Newtonsoft. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.Extensions.Xml namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Xml.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md index d0ecc5ea5..dfe3b5b93 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.Rendering.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Mvc.Rendering namespace contains extension methods that complements the Microsoft.AspNetCore.Mvc.Rendering namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.AspNetCore.Mvc.Rendering namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.rendering?view=aspnetcore-3.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md index c00f5f3f8..88db4bc3f 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.Mvc.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore.Mvc namespace contains both types and extension methods that complements the Cuemon.AspNetCore.Mvc namespace while being an addition to the Microsoft.AspNetCore.Mvc namespace. Provides a set of different cache busting strategies for ASP.NET Core MVC that can be easily customized. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.AspNetCore.Mvc namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.Mvc.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md index 7e9f24685..f2f4054e0 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md +++ b/docfx/api/namespaces/Cuemon.Extensions.AspNetCore.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.AspNetCore namespace contains both types and extension methods that complements the Cuemon.AspNetCore namespace while being an addition to the Microsoft.AspNetCore namespace. Provides an in-memory implementation of a throttling cache for ASP.NET Core. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Cuemon.AspNetCore namespace](https://docs.cuemon.net/api/aspnet/Cuemon.AspNetCore.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md b/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md index 0d969530c..6df1d3cc2 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Collections.Generic.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon.Collections.Generic namespace while being an addition to the System.Collections.Specialized namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Collections.Specialized namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Collections.Generic.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md b/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md index 508fcf733..ada5764da 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Collections.Specialized.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Collections.Specialized namespace contains extension methods that complements the Cuemon.Collections.Specialized namespace while being an addition to the System.Collections.Specialized namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Collections.Specialized namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Collections.Specialized.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md b/docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md index 01edee9a2..b922d8574 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Data.Integrity.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Data.Integrity namespace contains extension methods that complements the Cuemon.Data.Integrity namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Data.Integrity namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Data.Integrity.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Data.md b/docfx/api/namespaces/Cuemon.Extensions.Data.md index c7ddc9468..dd4aacd56 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Data.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Data.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Data namespace contains extension methods that complements the Cuemon.Data namespace while being an addition to the System.Data namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Data namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Data.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md b/docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md index 1484f2afd..51d88c2e7 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md +++ b/docfx/api/namespaces/Cuemon.Extensions.DependencyInjection.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.DependencyInjection namespace contains extension methods that complements the Microsoft.Extensions.DependencyInjection namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Microsoft.Extensions.DependencyInjection namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection?view=dotnet-plat-ext-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md index 9fa8f5757..e178394b8 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Diagnostics.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Diagnostics namespace contains extension methods that complements the Cuemon.Diagnostics namespace while being an addition to the System.Diagnostics namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Diagnostics namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Hosting.md b/docfx/api/namespaces/Cuemon.Extensions.Hosting.md index eddef1bf3..9d4e4003b 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Hosting.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Hosting.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Hosting namespace contains extension methods and features related to the Microsoft.Extensions.Hosting namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [Microsoft.Extensions.Hosting namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting?view=dotnet-plat-ext-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.IO.md b/docfx/api/namespaces/Cuemon.Extensions.IO.md index 54fb6fb62..28efe6242 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.IO.md +++ b/docfx/api/namespaces/Cuemon.Extensions.IO.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.IO namespace contains extension methods that complements the Cuemon.IO namespace while being an addition to the System.IO namespace. -Availability: NET Standard 2.0, NET Standard 2.1 +Availability: NET Standard 2.0, NET Standard 2.1, .NET 5.0 Complements: [Cuemon.IO namespace](https://docs.cuemon.net/api/dotnet/Cuemon.IO.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Net.Http.md b/docfx/api/namespaces/Cuemon.Extensions.Net.Http.md index 71f99078e..71c1e61c1 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Net.Http.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Net.Http.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Net.Http namespace contains both types and extension methods that complements the Cuemon.Net namespace. Includes support for both traditional and factory based ways of working with HttpMangager instances while also including a simple and lightweight implementation of the IHttpClientFactory interface named SlimHttpClientFactory (that provides "managed" HttpClient instances). -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Net/Http)\ diff --git a/docfx/api/namespaces/Cuemon.Extensions.Net.Security.md b/docfx/api/namespaces/Cuemon.Extensions.Net.Security.md index 76bffe1d9..cbe162418 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Net.Security.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Net.Security.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Net.Security namespace contains extension methods that provides a generic way to make a Uniform Resource Identifier signed and tampering protected. This could be used to make your own lightweight concept of a Azure shared access signatures (SAS). Originally part of Cuemon .NET Framework: https://github.com/gimlichael/CuemonNetFramework/blob/master/Cuemon.Web/Security/WebSecurityUtility.cs. Greatly simplified anno 2020. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Net/Security)\ diff --git a/docfx/api/namespaces/Cuemon.Extensions.Net.md b/docfx/api/namespaces/Cuemon.Extensions.Net.md index 7eb15d832..e97df1d87 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Net.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Net.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Net namespace contains both types and extension methods that complements the Cuemon.Net namespace while being an addition to the System.Net namespace. Includes support for both traditional and factory based ways of working with HttpMangager instances while also including a simple and lightweight implementation of the IHttpClientFactory interface named SlimHttpClientFactory (that provides "managed" HttpClient instances). -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Net namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Net.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md index eb49a6ebb..a7bbb93c7 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Converters.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Newtonsoft.Json.Converters namespace contains both types and extension methods that complements the Newtonsoft.Json.Converters namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Newtonsoft.Json.Converters namespace](https://www.newtonsoft.com/json/help/html/N_Newtonsoft_Json_Converters.htm) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md index f60dee302..4e6a5b48f 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Diagnostics.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Newtonsoft.Json.Diagnostics namespace contains extension methods that complements the Cuemon.Diagnostics namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Diagnostics namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md index 76be0e60e..bff68a885 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.Formatters.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Newtonsoft.Json.Formatters namespace contains types that are used to serialize and deserialize objects into and from JSON format using a generic signature. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Newtonsoft.Json.Serialization namespace](https://www.newtonsoft.com/json/help/html/N_Newtonsoft_Json_Serialization.htm) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md index 246d1380e..7ef6e033e 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Newtonsoft.Json.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Newtonsoft.Json namespace contains both types and extension methods that complements the Newtonsoft.Json namespace by adding new ways of working with JSON; both in terms of serialization and parsing. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Newtonsoft.Json namespace](https://www.newtonsoft.com/json/help/html/N_Newtonsoft_Json.htm) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Reflection.md b/docfx/api/namespaces/Cuemon.Extensions.Reflection.md index 687cbc116..afb29fef8 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Reflection.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Reflection.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Reflection namespace contains extension methods that complements the Cuemon.Reflection namespace while being an addition to the System.Reflection namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Reflection namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Reflection.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md b/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md index d7d188248..e584fd101 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Runtime.Caching.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Runtime.Caching namespace contains extension methods that complements the Cuemon.Runtime.Caching namespace by adding support for Memoization techniques and GetOrAdd convenience ; both with vast overloads and extended by the ICacheEnumerable{TKey} interface for loose coupling. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Runtime.Caching namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Runtime.Caching.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Text.md b/docfx/api/namespaces/Cuemon.Extensions.Text.md index f46016ef9..cd196cb58 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Text.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Text.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Text namespace contains extension methods that complements the Cuemon.Text namespace while being an addition to the System namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Text namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Text.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md b/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md index ee8629624..bd44b9081 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Threading.Tasks.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Threading.Tasks namespace contains extension methods that complements the System.Threading.Tasks namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Threading.Tasks namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Threading.md b/docfx/api/namespaces/Cuemon.Extensions.Threading.md index fc8c7cfad..e6121d392 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Threading.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Threading.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Threading namespace contains extension methods that complements the System.Threading namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Threading namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md index 13b9a1060..a8e853fa1 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Linq.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Xml.Linq namespace contains extension methods that complements the System namespace while being an addition to the System.Xml.Linq namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Extensions.Xml/Linq)\ diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md index 5a9032be1..f83b23b24 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Converters.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Xml.Serialization.Converters namespace contains extension methods that complements the Cuemon.Xml.Serialization.Converters namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Xml.Serialization.Converters namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.Serialization.Converters.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md index 8121a6dfc..054dd7ebb 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.Diagnostics.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Xml.Serialization.Diagnostics namespace contains extension methods that complements the Cuemon.Diagnostics namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Diagnostics namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Diagnostics.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md index 7585c1430..ec71dd2b7 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.Serialization.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Xml.Serialization namespace contains extension methods that complements the Cuemon.Xml.Serialization namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Xml.Serialization namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.Serialization.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xml.md b/docfx/api/namespaces/Cuemon.Extensions.Xml.md index adae5c5c1..2456c2712 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xml.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xml.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Xml namespace contains extension methods that complements the Cuemon.Xml namespace while being an addition to the System.Xml namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon.Xml namespace](https://docs.cuemon.net/api/dotnet/Cuemon.Xml.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.md index 060820a2a..289a76fa4 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core MVC and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.Mvc.Testing namespace. -Availability: NET Core 3.1 +Availability: NET Core 3.1, .NET 5.0 Complements: [Microsoft.AspNetCore.Mvc.Testing namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.testing?view=aspnetcore-3.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md index 09718b8f1..7c65d9835 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.AspNetCore.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.TestHost namespace. -Availability: NET Core 3.1 +Availability: NET Core 3.1, .NET 5.0 Complements: [Microsoft.AspNetCore.TestHost namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.testhost?view=aspnetcore-3.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md index 9a823f8ba..75a957658 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.Hosting.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Xunit.Hosting namespace contains types that provides a uniform way of doing unit testing that is used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Complements: [xUnit: Shared Context between Tests](https://xunit.net/docs/shared-context) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.Xunit.md b/docfx/api/namespaces/Cuemon.Extensions.Xunit.md index da0928724..6641e0d4c 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.Xunit.md +++ b/docfx/api/namespaces/Cuemon.Extensions.Xunit.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions.Xunit namespace contains types that provides a uniform way of doing unit testing. The namespace relates to the Xunit.Abstractions namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [xUnit: Capturing Output](https://xunit.net/docs/capturing-output) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Extensions.md b/docfx/api/namespaces/Cuemon.Extensions.md index 3952a6884..07de24eb8 100644 --- a/docfx/api/namespaces/Cuemon.Extensions.md +++ b/docfx/api/namespaces/Cuemon.Extensions.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Extensions namespace contains extension methods that complements the Cuemon namespace while being an addition to the System namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [Cuemon namespace](https://docs.cuemon.net/api/dotnet/Cuemon.html) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Globalization.md b/docfx/api/namespaces/Cuemon.Globalization.md index 9a0d9460c..549d677a5 100644 --- a/docfx/api/namespaces/Cuemon.Globalization.md +++ b/docfx/api/namespaces/Cuemon.Globalization.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Globalization namespace contains types that focuses on culture-related information, including language, country/region and localized resources useful for writing globalized (internationalized) applications. The namespace is an addition to the System.Globalization namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Globalization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.globalization?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.IO.md b/docfx/api/namespaces/Cuemon.IO.md index 7a5ad3a07..2fabbdc65 100644 --- a/docfx/api/namespaces/Cuemon.IO.md +++ b/docfx/api/namespaces/Cuemon.IO.md @@ -4,6 +4,6 @@ summary: *content --- The Cuemon.IO namespace contains types primarily focusing on configuration options for IO related operations. The namespace is an addition to the System.IO namespace. -Availability: NET Standard 2.0, NET Standard 2.1 +Availability: NET Standard 2.0, NET Standard 2.1, .NET 5.0 Complements: [System.IO namespace](https://docs.microsoft.com/en-us/dotnet/api/system.io?view=netstandard-2.1) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Messaging.md b/docfx/api/namespaces/Cuemon.Messaging.md index 1fcd0c413..d14400c3f 100644 --- a/docfx/api/namespaces/Cuemon.Messaging.md +++ b/docfx/api/namespaces/Cuemon.Messaging.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Messaging namespace contains types that assist in more advanced scenarios such as CQRS, microservices and event-driven architecture. The namespace is an addition to the System.Messaging namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Messaging namespace](https://docs.microsoft.com/en-us/dotnet/api/system.messaging?view=netframework-4.6.1) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Net.Http.md b/docfx/api/namespaces/Cuemon.Net.Http.md index 8198a4d0c..f9a596aef 100644 --- a/docfx/api/namespaces/Cuemon.Net.Http.md +++ b/docfx/api/namespaces/Cuemon.Net.Http.md @@ -4,6 +4,6 @@ summary: *content --- The Cuemon.Net.Http namespace contains types that is compliant with RFC 7231, section 4: Request methods and RFC 5789, section 2: Patch method while allowing custom definitions as well. The namespace is an addition to the System.Net.Http namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Net.Http namespace](https://docs.microsoft.com/en-us/dotnet/api/system.net.http?view=netstandard-2.0) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Net.Mail.md b/docfx/api/namespaces/Cuemon.Net.Mail.md index 003a978c1..d0d8c583b 100644 --- a/docfx/api/namespaces/Cuemon.Net.Mail.md +++ b/docfx/api/namespaces/Cuemon.Net.Mail.md @@ -4,6 +4,6 @@ summary: *content --- The Cuemon.Net.Mail namespace contains types that makes delivery of mail a piece of cake. The namespace is an addition to the System.Net.Mail namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Net.Mail namespace](https://docs.microsoft.com/en-us/dotnet/api/system.net.mail?view=netstandard-2.0) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Net.md b/docfx/api/namespaces/Cuemon.Net.md index 7d53e18d2..357b7f670 100644 --- a/docfx/api/namespaces/Cuemon.Net.md +++ b/docfx/api/namespaces/Cuemon.Net.md @@ -4,6 +4,6 @@ summary: *content --- The Cuemon.Net namespace contains types that provides a simple programming interface for HTTP and SMTP protocols. The namespace is an addition to the System.Net namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Net namespace](https://docs.microsoft.com/en-us/dotnet/api/system.net?view=netstandard-2.0) \ No newline at end of file diff --git a/docfx/api/namespaces/Cuemon.Reflection.md b/docfx/api/namespaces/Cuemon.Reflection.md index 099998e6a..8cedf1af7 100644 --- a/docfx/api/namespaces/Cuemon.Reflection.md +++ b/docfx/api/namespaces/Cuemon.Reflection.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Reflection namespace contains types that focuses on working natural with the hidden gems of reflection in order to retrieve information about assemblies, members, parameters, and different versioning schemes that support both traditional and semantic. The namespace is an addition to the System.Reflection namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Reflection namespace](https://docs.microsoft.com/en-us/dotnet/api/system.reflection?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Resilience.md b/docfx/api/namespaces/Cuemon.Resilience.md index 1def8a5ef..400d08ab1 100644 --- a/docfx/api/namespaces/Cuemon.Resilience.md +++ b/docfx/api/namespaces/Cuemon.Resilience.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Resilience namespace contains types related to applying transient fault handling to existing code using intuitively named methods taking both Action{..} and Func{..} delegates to provide a lightweight resilience framework. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Github branches: 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Resilience)\ diff --git a/docfx/api/namespaces/Cuemon.Runtime.Caching.md b/docfx/api/namespaces/Cuemon.Runtime.Caching.md index a6afe57be..b56f3d367 100644 --- a/docfx/api/namespaces/Cuemon.Runtime.Caching.md +++ b/docfx/api/namespaces/Cuemon.Runtime.Caching.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Runtime.Caching namespace contains types related to interfaces for generic caching in applications while providing a concrete in-memory cache implementation named SlimMemoryCache. The namespace is an addition to the System.Runtime.Caching namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Runtime.Caching namespace](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.caching?view=netframework-4.6.1) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md b/docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md index 801aa37e2..12e011e2d 100644 --- a/docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md +++ b/docfx/api/namespaces/Cuemon.Runtime.Serialization.Formatters.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Runtime.Serialization.Formatters namespace contains types that are used to serialize and deserialize objects into and from a generic type. The namespace is an addition to the System.Runtime.Serialization namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Runtime.Serialization.Formatters namespace](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Runtime.Serialization.md b/docfx/api/namespaces/Cuemon.Runtime.Serialization.md index 312cbc133..7471f8a38 100644 --- a/docfx/api/namespaces/Cuemon.Runtime.Serialization.md +++ b/docfx/api/namespaces/Cuemon.Runtime.Serialization.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Runtime.Serialization namespace contains types that are used to serialize objects into a hierarchy of nodes. The namespace is an addition to the System.Runtime.Serialization namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Runtime.Serialization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Runtime.md b/docfx/api/namespaces/Cuemon.Runtime.md index 84b75ec29..82a037734 100644 --- a/docfx/api/namespaces/Cuemon.Runtime.md +++ b/docfx/api/namespaces/Cuemon.Runtime.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Runtime namespace contains types that support different namespaces such as the Cuemon, Cuemon.Data, Cuemon.Net, and the Cuemon.Runtime.Caching namespaces. The namespace is an addition to the System.Runtime namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Runtime namespace](https://docs.microsoft.com/en-us/dotnet/api/system.runtime?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Security.Cryptography.md b/docfx/api/namespaces/Cuemon.Security.Cryptography.md index c40e6ed9a..0cbdf5ed1 100644 --- a/docfx/api/namespaces/Cuemon.Security.Cryptography.md +++ b/docfx/api/namespaces/Cuemon.Security.Cryptography.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Security.Cryptography namespace contains types related to cryptographic hashing (both keyed and non-keyed) and a ready-to-use implementation of the Advanced Encryption Standard (AES) symmetric algorithm. The namespace is an addition to the System.Security.Cryptography namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Security.Cryptography namespace](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Security.md b/docfx/api/namespaces/Cuemon.Security.md index c96f92eab..8ee178beb 100644 --- a/docfx/api/namespaces/Cuemon.Security.md +++ b/docfx/api/namespaces/Cuemon.Security.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Security namespace contains types related to hashing (both non-cryptographic and CRC) and has the base class from which all implementations of hash algorithms and checksums should derive. The namespace is an addition to the System.Security namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Security namespace](https://docs.microsoft.com/en-us/dotnet/api/system.security?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Text.md b/docfx/api/namespaces/Cuemon.Text.md index 110899806..89cc3dc70 100644 --- a/docfx/api/namespaces/Cuemon.Text.md +++ b/docfx/api/namespaces/Cuemon.Text.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Text namespace contains types tailored to ease the pain of working with encodings, BOM, parsing, preamble sequences and stems. Also includes way to conform to a uniform way of turning strings into objects of a particular type. The namespace is an addition to the System.Text namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Text namespace](https://docs.microsoft.com/en-us/dotnet/api/system.text?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Threading.md b/docfx/api/namespaces/Cuemon.Threading.md index 03f85f3ba..b30396695 100644 --- a/docfx/api/namespaces/Cuemon.Threading.md +++ b/docfx/api/namespaces/Cuemon.Threading.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Threading namespace contains types related to working with long-running concurrent loops and regions that utilizes both synchronous and asynchronous delegates. The namespace is an addition to the System.Threading namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Threading namespace](https://docs.microsoft.com/en-us/dotnet/api/system.threading?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Xml.Linq.md b/docfx/api/namespaces/Cuemon.Xml.Linq.md index 367fe8674..a478802c8 100644 --- a/docfx/api/namespaces/Cuemon.Xml.Linq.md +++ b/docfx/api/namespaces/Cuemon.Xml.Linq.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Xml.Linq namespace contains types that is used internally by this and related assemblies and is not intended to be used directly from your code. The namespace is an addition to the System.Xml.Linq namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Xml.Linq namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.linq?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md b/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md index 0ab7c7f64..fef54ded0 100644 --- a/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md +++ b/docfx/api/namespaces/Cuemon.Xml.Serialization.Converters.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Xml.Serialization.Converters namespace contains types tailored to resemble the [JsonConverter](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonConverter.htm) except we convert objects to and from XML. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Related: [Cuemon.Extensions.Xml.Serialization.Converters namespace](https://docs.cuemon.net/api/dotnet/ext/Cuemon.Extensions.Xml.Serialization.Converters.html) 📘 diff --git a/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md b/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md index b6ff49267..4713fe8ba 100644 --- a/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md +++ b/docfx/api/namespaces/Cuemon.Xml.Serialization.Formatters.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Xml.Serialization.Formatters namespace contains types that are used to serialize and deserialize objects into and from XML format using a generic signature. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Github branches 🌱\ [development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.Xml/Serialization/Formatters)\ diff --git a/docfx/api/namespaces/Cuemon.Xml.Serialization.md b/docfx/api/namespaces/Cuemon.Xml.Serialization.md index fcfc0dd96..0a8ed138a 100644 --- a/docfx/api/namespaces/Cuemon.Xml.Serialization.md +++ b/docfx/api/namespaces/Cuemon.Xml.Serialization.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Xml.Serialization namespace contains types that are used to serialize and deserialize objects into and from XML format. The namespace is an addition to the System.Xml.Serialization namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Xml.Serialization namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Xml.XPath.md b/docfx/api/namespaces/Cuemon.Xml.XPath.md index c639bfa14..1f13bf0b5 100644 --- a/docfx/api/namespaces/Cuemon.Xml.XPath.md +++ b/docfx/api/namespaces/Cuemon.Xml.XPath.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Xml.XPath namespace contains types related to easing creation of XPathDocument instances. The namespace is an addition to the System.Xml.XPath namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Xml.XPath namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml.xpath?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.Xml.md b/docfx/api/namespaces/Cuemon.Xml.md index 61e7ef534..0d9fbd408 100644 --- a/docfx/api/namespaces/Cuemon.Xml.md +++ b/docfx/api/namespaces/Cuemon.Xml.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon.Xml namespace contains types related to encoding, converting and serialization. The included lightweight XML serializer framework offers same flexibility as the one provided by the JSON equivalent from Newtonsoft. The namespace is an addition to the System.Xml namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System.Xml namespace](https://docs.microsoft.com/en-us/dotnet/api/system.xml?view=netstandard-2.0) 🔗 diff --git a/docfx/api/namespaces/Cuemon.md b/docfx/api/namespaces/Cuemon.md index cdd0a71a8..a1a68a211 100644 --- a/docfx/api/namespaces/Cuemon.md +++ b/docfx/api/namespaces/Cuemon.md @@ -4,7 +4,7 @@ summary: *content --- The Cuemon namespace contains fundamental types such as value and reference types, factories and utility classes, interfaces, attributes and feature rich delegates to support functional programming to a whole new level. The namespace is an addition to the System namespace. -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, .NET 5.0 Complements: [System namespace](https://docs.microsoft.com/en-us/dotnet/api/system?view=netstandard-2.0) 🔗 diff --git a/docfx/index.md b/docfx/index.md deleted file mode 100644 index 9e21b7f68..000000000 --- a/docfx/index.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Technical documentation, API, and code examples -documentType: index ---- -
-
-
-
-
-
-

Cuemon for .NET 6.0.0-preview

-

Cuemon for .NET is an open-source project (MIT license) that targets and complements the Microsoft .NET platform. It provides vast ways of possibilities for all breeds of coders, programmers, developers and the likes thereof. Ideal for .NET, .NET Standard, .NET Core, Universal Windows Platform and .NET Framework 4.6.1 and newer.

-

It is, by heart, free, flexible and built to extend and boost your agile codebelt.

-
-
- - - -
-
-
-
\ No newline at end of file diff --git a/docfx/templates/cuemon/index.html.tmpl b/docfx/templates/cuemon/index.html.tmpl deleted file mode 100644 index 6953b6855..000000000 --- a/docfx/templates/cuemon/index.html.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} -{{!include(/^styles/.*/)}} -{{!include(/^fonts/.*/)}} -{{!include(favicon.ico)}} -{{!include(logo.svg)}} - - - - {{>partials/head}} - -
- {{{conceptual}}} - {{^_disableFooter}} - {{>partials/footer}} - {{/_disableFooter}} -
- {{>partials/scripts}} - - \ No newline at end of file diff --git a/docfx/templates/cuemon/partials/logo.tmpl.partial b/docfx/templates/cuemon/partials/logo.tmpl.partial new file mode 100644 index 000000000..0e4e54801 --- /dev/null +++ b/docfx/templates/cuemon/partials/logo.tmpl.partial @@ -0,0 +1,5 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} + + + {{_appName}} + \ No newline at end of file From 184834f71d6ec5783efa13aff6fe2337f2b1e9a3 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 26 Feb 2021 00:09:30 +0100 Subject: [PATCH 371/385] Rename from Microsoft.AspNetCore.Razor --> Microsoft.AspNetCore.Razor.TagHelpers. --- Cuemon.sln | 2 +- .../CdnTagHelper.cs | 0 .../CdnTagHelperOptions.cs | 0 .../CdnUriScheme.cs | 0 .../Cuemon.AspNetCore.Razor.TagHelpers.csproj} | 6 +++--- .../ImageCdnTagHelper.cs | 0 .../LinkCdnTagHelper.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../ScriptCdnTagHelper.cs | 0 9 files changed, 4 insertions(+), 4 deletions(-) rename src/{Cuemon.AspNetCore.Razor/TagHelpers => Cuemon.AspNetCore.Razor.TagHelpers}/CdnTagHelper.cs (100%) rename src/{Cuemon.AspNetCore.Razor/TagHelpers => Cuemon.AspNetCore.Razor.TagHelpers}/CdnTagHelperOptions.cs (100%) rename src/{Cuemon.AspNetCore.Razor/TagHelpers => Cuemon.AspNetCore.Razor.TagHelpers}/CdnUriScheme.cs (100%) rename src/{Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj => Cuemon.AspNetCore.Razor.TagHelpers/Cuemon.AspNetCore.Razor.TagHelpers.csproj} (59%) rename src/{Cuemon.AspNetCore.Razor/TagHelpers => Cuemon.AspNetCore.Razor.TagHelpers}/ImageCdnTagHelper.cs (100%) rename src/{Cuemon.AspNetCore.Razor/TagHelpers => Cuemon.AspNetCore.Razor.TagHelpers}/LinkCdnTagHelper.cs (100%) rename src/{Cuemon.AspNetCore.Razor => Cuemon.AspNetCore.Razor.TagHelpers}/Properties/AssemblyInfo.cs (100%) rename src/{Cuemon.AspNetCore.Razor/TagHelpers => Cuemon.AspNetCore.Razor.TagHelpers}/ScriptCdnTagHelper.cs (100%) diff --git a/Cuemon.sln b/Cuemon.sln index 1770b9afa..ed4a866e5 100644 --- a/Cuemon.sln +++ b/Cuemon.sln @@ -43,7 +43,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Extensions.AspNetCor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.AspNetCore.Authentication", "src\Cuemon.AspNetCore.Authentication\Cuemon.AspNetCore.Authentication.csproj", "{A10ADF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.AspNetCore.Razor", "src\Cuemon.AspNetCore.Razor\Cuemon.AspNetCore.Razor.csproj", "{A30ADF91-E7C7-4CB4-A39D-E1A5374C5602}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.AspNetCore.Razor.TagHelpers", "src\Cuemon.AspNetCore.Razor.TagHelpers\Cuemon.AspNetCore.Razor.TagHelpers.csproj", "{A30ADF91-E7C7-4CB4-A39D-E1A5374C5602}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cuemon.Core.Tests", "test\Cuemon.Core.Tests\Cuemon.Core.Tests.csproj", "{CDE37A87-B35E-4F9B-9C5A-32E9B22A1B69}" EndProject diff --git a/src/Cuemon.AspNetCore.Razor/TagHelpers/CdnTagHelper.cs b/src/Cuemon.AspNetCore.Razor.TagHelpers/CdnTagHelper.cs similarity index 100% rename from src/Cuemon.AspNetCore.Razor/TagHelpers/CdnTagHelper.cs rename to src/Cuemon.AspNetCore.Razor.TagHelpers/CdnTagHelper.cs diff --git a/src/Cuemon.AspNetCore.Razor/TagHelpers/CdnTagHelperOptions.cs b/src/Cuemon.AspNetCore.Razor.TagHelpers/CdnTagHelperOptions.cs similarity index 100% rename from src/Cuemon.AspNetCore.Razor/TagHelpers/CdnTagHelperOptions.cs rename to src/Cuemon.AspNetCore.Razor.TagHelpers/CdnTagHelperOptions.cs diff --git a/src/Cuemon.AspNetCore.Razor/TagHelpers/CdnUriScheme.cs b/src/Cuemon.AspNetCore.Razor.TagHelpers/CdnUriScheme.cs similarity index 100% rename from src/Cuemon.AspNetCore.Razor/TagHelpers/CdnUriScheme.cs rename to src/Cuemon.AspNetCore.Razor.TagHelpers/CdnUriScheme.cs diff --git a/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj b/src/Cuemon.AspNetCore.Razor.TagHelpers/Cuemon.AspNetCore.Razor.TagHelpers.csproj similarity index 59% rename from src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj rename to src/Cuemon.AspNetCore.Razor.TagHelpers/Cuemon.AspNetCore.Razor.TagHelpers.csproj index 24ef063c5..780dd263d 100644 --- a/src/Cuemon.AspNetCore.Razor/Cuemon.AspNetCore.Razor.csproj +++ b/src/Cuemon.AspNetCore.Razor.TagHelpers/Cuemon.AspNetCore.Razor.TagHelpers.csproj @@ -6,9 +6,9 @@ - Cuemon.AspNetCore.Razor - Cuemon.AspNetCore.Razor - The Cuemon.AspNetCore.Razor namespace contains features related to the Microsoft.AspNetCore.Razor namespace. + Cuemon.AspNetCore.Razor.TagHelpers + Cuemon.AspNetCore.Razor.TagHelpers + The Cuemon.AspNetCore.Razor.TagHelpers namespace contains types tailored tag helper implementations. The namespace is an addition to the Microsoft.AspNetCore.Razor.TagHelpers namespace. cdn-tag-helper cdn-uri-scheme image-cdn-tag-helper link-cdn-tag-helper script-cdn-tag-helper diff --git a/src/Cuemon.AspNetCore.Razor/TagHelpers/ImageCdnTagHelper.cs b/src/Cuemon.AspNetCore.Razor.TagHelpers/ImageCdnTagHelper.cs similarity index 100% rename from src/Cuemon.AspNetCore.Razor/TagHelpers/ImageCdnTagHelper.cs rename to src/Cuemon.AspNetCore.Razor.TagHelpers/ImageCdnTagHelper.cs diff --git a/src/Cuemon.AspNetCore.Razor/TagHelpers/LinkCdnTagHelper.cs b/src/Cuemon.AspNetCore.Razor.TagHelpers/LinkCdnTagHelper.cs similarity index 100% rename from src/Cuemon.AspNetCore.Razor/TagHelpers/LinkCdnTagHelper.cs rename to src/Cuemon.AspNetCore.Razor.TagHelpers/LinkCdnTagHelper.cs diff --git a/src/Cuemon.AspNetCore.Razor/Properties/AssemblyInfo.cs b/src/Cuemon.AspNetCore.Razor.TagHelpers/Properties/AssemblyInfo.cs similarity index 100% rename from src/Cuemon.AspNetCore.Razor/Properties/AssemblyInfo.cs rename to src/Cuemon.AspNetCore.Razor.TagHelpers/Properties/AssemblyInfo.cs diff --git a/src/Cuemon.AspNetCore.Razor/TagHelpers/ScriptCdnTagHelper.cs b/src/Cuemon.AspNetCore.Razor.TagHelpers/ScriptCdnTagHelper.cs similarity index 100% rename from src/Cuemon.AspNetCore.Razor/TagHelpers/ScriptCdnTagHelper.cs rename to src/Cuemon.AspNetCore.Razor.TagHelpers/ScriptCdnTagHelper.cs From d4e80a3e3981715cd6d545bbc2859fb748274aac Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 26 Feb 2021 00:09:46 +0100 Subject: [PATCH 372/385] Updated nginx version. --- Dockerfile.docfx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.docfx b/Dockerfile.docfx index a9a550192..f99f06682 100644 --- a/Dockerfile.docfx +++ b/Dockerfile.docfx @@ -1,6 +1,6 @@ # escape=` -FROM nginx:1.19.2 AS base +FROM nginx:1.19.7 AS base RUN rm -rf /usr/share/nginx/html/* FROM mono:6.10.0.104 AS build From 01cdf31eee2978f7493b13cba9d5fbd5978a1291 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 26 Feb 2021 00:10:54 +0100 Subject: [PATCH 373/385] Updated description and tags. --- .../Cuemon.AspNetCore.Authentication.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj index 71c6b04d8..c7c46be4e 100644 --- a/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj +++ b/src/Cuemon.AspNetCore.Authentication/Cuemon.AspNetCore.Authentication.csproj @@ -8,8 +8,8 @@ Cuemon.AspNetCore.Authentication Cuemon.AspNetCore.Authentication - The Cuemon.AspNetCore.Authentication namespace contains implementations of authentication forms and features related to the Cuemon.AspNetCore namespace. - basic-authentication digest-access-authentication hmac-authentication + The Cuemon.AspNetCore.Authentication namespace contains types that enable support for authentication using the concept of an Authenticator, AuthorizationHeader and (to tie the knots) an AuthorizationHeaderBuilder. The namespace is an addition to the Microsoft.AspNetCore.Authentication namespace. + basic-authentication basic-authentication-middleware digest-access-authentication digest-authentication-middleware hmac-authentication hmac-authentication-middleware authenticator authorization-header authorization-header-builder nonce-tracker From 329279d225ea74659ff2b8039cca45214e6abf89 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Fri, 26 Feb 2021 01:13:25 +0100 Subject: [PATCH 374/385] Fixed path. --- .../namespaces/Cuemon.AspNetCore.Authentication.Basic.md | 6 +++--- .../namespaces/Cuemon.AspNetCore.Authentication.Digest.md | 6 +++--- .../api/namespaces/Cuemon.AspNetCore.Authentication.Hmac.md | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Basic.md b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Basic.md index 7ffa1f5a7..b94d60232 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Basic.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Basic.md @@ -7,9 +7,9 @@ The Cuemon.AspNetCore.Authentication.Basic namespace contains types that enable Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Github branches 🌱\ -[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Authentication)\ -[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Authentication)\ -[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Authentication) +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Authentication/Basic)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Authentication/Basic)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Authentication/Basic) NuGet packages 📦\ [Cuemon.AspNetCore.Authentication (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Authentication)\ diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Digest.md b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Digest.md index 8cb1bec78..46a052c31 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Digest.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Digest.md @@ -7,9 +7,9 @@ The Cuemon.AspNetCore.Authentication.Digest namespace contains types that enable Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Github branches 🌱\ -[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Authentication)\ -[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Authentication)\ -[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Authentication) +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Authentication/Digest)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Authentication/Digest)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Authentication/Digest) NuGet packages 📦\ [Cuemon.AspNetCore.Authentication (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Authentication)\ diff --git a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Hmac.md b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Hmac.md index 93b0c715d..40c57b8c3 100644 --- a/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Hmac.md +++ b/docfx/api/namespaces/Cuemon.AspNetCore.Authentication.Hmac.md @@ -7,9 +7,9 @@ The Cuemon.AspNetCore.Authentication.Hmac namespace contains types that enable s Availability: NET Standard 2.0, NET Core 3.0, .NET 5.0 Github branches 🌱\ -[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Authentication)\ -[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Authentication)\ -[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Authentication) +[development](https://github.com/gimlichael/Cuemon/tree/development/src/Cuemon.AspNetCore.Authentication/Hmac)\ +[release](https://github.com/gimlichael/Cuemon/tree/release/src/Cuemon.AspNetCore.Authentication/Hmac)\ +[master](https://github.com/gimlichael/Cuemon/tree/master/src/Cuemon.AspNetCore.Authentication/Hmac) NuGet packages 📦\ [Cuemon.AspNetCore.Authentication (CI)](https://nuget.cuemon.net/packages/Cuemon.AspNetCore.Authentication)\ From a8b489b4bbf2e2c7f44a65f6e249ef64f8710798 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Fri, 26 Feb 2021 01:36:34 +0100 Subject: [PATCH 375/385] Added release notes. --- .../Properties/PackageReleaseNotes.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/Cuemon.AspNetCore.Razor.TagHelpers/Properties/PackageReleaseNotes.txt diff --git a/src/Cuemon.AspNetCore.Razor.TagHelpers/Properties/PackageReleaseNotes.txt b/src/Cuemon.AspNetCore.Razor.TagHelpers/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..ffcac726d --- /dev/null +++ b/src/Cuemon.AspNetCore.Razor.TagHelpers/Properties/PackageReleaseNotes.txt @@ -0,0 +1,6 @@ +Version: 6.0.0 +Availability: NET Standard 2.0, NET Core 3.0, NET 5.0 +  +# Upgrade Steps +- Any former extension methods of the Cuemon.AspNetCore.Razor.TagHelpers namespace was merged into the Cuemon.Extensions.Core namespace +  \ No newline at end of file From 8ce4451afd9f08f1f87b4d60c41ba15e634f9325 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 27 Feb 2021 01:34:41 +0100 Subject: [PATCH 376/385] Renamed for clarity. --- .../{NonceTracker.cs => MemoryNonceTracker.cs} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename src/Cuemon.AspNetCore.Authentication/{NonceTracker.cs => MemoryNonceTracker.cs} (91%) diff --git a/src/Cuemon.AspNetCore.Authentication/NonceTracker.cs b/src/Cuemon.AspNetCore.Authentication/MemoryNonceTracker.cs similarity index 91% rename from src/Cuemon.AspNetCore.Authentication/NonceTracker.cs rename to src/Cuemon.AspNetCore.Authentication/MemoryNonceTracker.cs index 8e2801d0b..be7e4fd31 100644 --- a/src/Cuemon.AspNetCore.Authentication/NonceTracker.cs +++ b/src/Cuemon.AspNetCore.Authentication/MemoryNonceTracker.cs @@ -7,21 +7,21 @@ namespace Cuemon.AspNetCore.Authentication { /// - /// Provides a default implementation of the interface. + /// Provides a default in-memory implementation of the interface. /// /// /// - public class NonceTracker : Disposable, INonceTracker + public class MemoryNonceTracker : Disposable, INonceTracker { private readonly ConcurrentDictionary _entries = new ConcurrentDictionary(); private readonly Timer _expirationTimer; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public NonceTracker() + public MemoryNonceTracker() { - _expirationTimer = TimerFactory.CreateNonCapturingTimer(state => ((NonceTracker)state).OnAutomatedSweepCleanup(), this, TimeSpan.FromMinutes(15), TimeSpan.FromHours(1)); + _expirationTimer = TimerFactory.CreateNonCapturingTimer(state => ((MemoryNonceTracker)state).OnAutomatedSweepCleanup(), this, TimeSpan.FromMinutes(15), TimeSpan.FromHours(1)); } /// From 2b87d596c1e1b1f3ff284f1ee27ff57604997b45 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 27 Feb 2021 01:50:32 +0100 Subject: [PATCH 377/385] Moved to more appropriate assembly/namespace. --- src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj | 4 ++++ .../Configuration/DynamicCacheBusting.cs | 3 +-- .../Configuration/DynamicCacheBustingOptions.cs | 5 ++--- .../Configuration/AssemblyCacheBusting.cs | 2 +- .../Configuration/AssemblyCacheBustingOptions.cs | 3 +-- .../Configuration/ServiceCollectionExtensions.cs | 3 +-- 6 files changed, 10 insertions(+), 10 deletions(-) rename src/{Cuemon.AspNetCore.Mvc => Cuemon.AspNetCore}/Configuration/DynamicCacheBusting.cs (96%) rename src/{Cuemon.AspNetCore.Mvc => Cuemon.AspNetCore}/Configuration/DynamicCacheBustingOptions.cs (95%) rename src/{Cuemon.Extensions.AspNetCore.Mvc => Cuemon.Extensions.AspNetCore}/Configuration/AssemblyCacheBusting.cs (96%) rename src/{Cuemon.Extensions.AspNetCore.Mvc => Cuemon.Extensions.AspNetCore}/Configuration/AssemblyCacheBustingOptions.cs (96%) rename src/{Cuemon.Extensions.AspNetCore.Mvc => Cuemon.Extensions.AspNetCore}/Configuration/ServiceCollectionExtensions.cs (95%) diff --git a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj index 12c9fe0ca..211110988 100644 --- a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj +++ b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj @@ -21,4 +21,8 @@ + + + + \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs b/src/Cuemon.AspNetCore/Configuration/DynamicCacheBusting.cs similarity index 96% rename from src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs rename to src/Cuemon.AspNetCore/Configuration/DynamicCacheBusting.cs index 16e8f365c..41cb66a82 100644 --- a/src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBusting.cs +++ b/src/Cuemon.AspNetCore/Configuration/DynamicCacheBusting.cs @@ -1,9 +1,8 @@ using System; -using Cuemon.AspNetCore.Configuration; using Cuemon.Configuration; using Microsoft.Extensions.Options; -namespace Cuemon.AspNetCore.Mvc.Configuration +namespace Cuemon.AspNetCore.Configuration { /// /// Provides cache-busting capabilities on a duration based interval. This class cannot be inherited. diff --git a/src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs b/src/Cuemon.AspNetCore/Configuration/DynamicCacheBustingOptions.cs similarity index 95% rename from src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs rename to src/Cuemon.AspNetCore/Configuration/DynamicCacheBustingOptions.cs index 6b1f9f8a0..39a955cc0 100644 --- a/src/Cuemon.AspNetCore.Mvc/Configuration/DynamicCacheBustingOptions.cs +++ b/src/Cuemon.AspNetCore/Configuration/DynamicCacheBustingOptions.cs @@ -1,10 +1,9 @@ using System; -using Cuemon.AspNetCore.Configuration; -namespace Cuemon.AspNetCore.Mvc.Configuration +namespace Cuemon.AspNetCore.Configuration { /// - /// Specifies options that is related to operations. + /// Specifies options that is related to operations. /// /// public class DynamicCacheBustingOptions : CacheBustingOptions diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBusting.cs b/src/Cuemon.Extensions.AspNetCore/Configuration/AssemblyCacheBusting.cs similarity index 96% rename from src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBusting.cs rename to src/Cuemon.Extensions.AspNetCore/Configuration/AssemblyCacheBusting.cs index e95fd3c05..8d365ee78 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBusting.cs +++ b/src/Cuemon.Extensions.AspNetCore/Configuration/AssemblyCacheBusting.cs @@ -4,7 +4,7 @@ using Cuemon.Security.Cryptography; using Microsoft.Extensions.Options; -namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration +namespace Cuemon.Extensions.AspNetCore.Configuration { /// /// Provides cache-busting capabilities from an Assembly. This class cannot be inherited. diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs b/src/Cuemon.Extensions.AspNetCore/Configuration/AssemblyCacheBustingOptions.cs similarity index 96% rename from src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs rename to src/Cuemon.Extensions.AspNetCore/Configuration/AssemblyCacheBustingOptions.cs index 18116105e..37da94a29 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/AssemblyCacheBustingOptions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Configuration/AssemblyCacheBustingOptions.cs @@ -1,9 +1,8 @@ using System.Reflection; using Cuemon.AspNetCore.Configuration; -using Cuemon.AspNetCore.Mvc.Configuration; using Cuemon.Security.Cryptography; -namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration +namespace Cuemon.Extensions.AspNetCore.Configuration { /// /// Specifies options that is related to operations. diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore/Configuration/ServiceCollectionExtensions.cs similarity index 95% rename from src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs rename to src/Cuemon.Extensions.AspNetCore/Configuration/ServiceCollectionExtensions.cs index 2ab785ed1..43beb86a2 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Configuration/ServiceCollectionExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore/Configuration/ServiceCollectionExtensions.cs @@ -1,8 +1,7 @@ using Cuemon.AspNetCore.Configuration; -using Cuemon.AspNetCore.Mvc.Configuration; using Microsoft.Extensions.DependencyInjection; -namespace Cuemon.Extensions.AspNetCore.Mvc.Configuration +namespace Cuemon.Extensions.AspNetCore.Configuration { /// /// Extension methods for the interface. From 573acdf4760cea72ec5689be5b210c7a2e311618 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 27 Feb 2021 01:52:56 +0100 Subject: [PATCH 378/385] Clarity changes. --- .../ServiceCollectionExtensions.cs | 4 ++-- .../Cuemon.Extensions.AspNetCore.csproj | 1 + .../DigestAccessAuthenticationMiddlewareTest.cs | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs index 08bb92956..ea3085123 100644 --- a/src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs @@ -9,14 +9,14 @@ namespace Cuemon.Extensions.AspNetCore.Authentication public static class ServiceCollectionExtensions { /// - /// Adds a service to the specified . + /// Adds a service to the specified . /// /// The to add services to. /// An that can be used to further configure other services. public static IServiceCollection AddDigestAccessAuthenticationNonceTracker(this IServiceCollection services) { Validator.ThrowIfNull(services, nameof(services)); - services.AddSingleton(); + services.AddSingleton(); return services; } } diff --git a/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj b/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj index 2c17a48ae..dfc529316 100644 --- a/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj +++ b/src/Cuemon.Extensions.AspNetCore/Cuemon.Extensions.AspNetCore.csproj @@ -19,6 +19,7 @@ + \ No newline at end of file diff --git a/test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs b/test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs index 5227df1d2..001e418d7 100644 --- a/test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs +++ b/test/Cuemon.AspNetCore.Authentication.Tests/DigestAccessAuthenticationMiddlewareTest.cs @@ -54,7 +54,7 @@ public async Task InvokeAsync_ShouldNotBeAuthenticated() o.RequireSecureConnection = false; }); services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); - services.AddDigestAccessAuthenticationNonceTracker(); + services.AddInMemoryDigestAuthenticationNonceTracker(); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; @@ -99,7 +99,7 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeader( o.RequireSecureConnection = false; }); services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); - services.AddDigestAccessAuthenticationNonceTracker(); + services.AddInMemoryDigestAuthenticationNonceTracker(); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; @@ -171,7 +171,7 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeaderN o.RequireSecureConnection = false; }); services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); - services.AddDigestAccessAuthenticationNonceTracker(); + services.AddInMemoryDigestAuthenticationNonceTracker(); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; @@ -242,7 +242,7 @@ public async Task InvokeAsync_ShouldAuthenticateWhenApplyingAuthorizationHeaderW o.RequireSecureConnection = false; }); services.AddFakeHttpContextAccessor(ServiceLifetime.Singleton); - services.AddDigestAccessAuthenticationNonceTracker(); + services.AddInMemoryDigestAuthenticationNonceTracker(); })) { var context = middleware.ServiceProvider.GetRequiredService().HttpContext; From 84070083b99e490ef5c329afebf84d9590139721 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 27 Feb 2021 02:10:58 +0100 Subject: [PATCH 379/385] Updated release notes. --- .../Properties/PackageReleaseNotes.txt | 37 +++++++++++++++++++ .../Cuemon.AspNetCore.Mvc.csproj | 4 -- .../Properties/PackageReleaseNotes.txt | 7 ++-- .../Properties/PackageReleaseNotes.txt | 13 ++++++- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- 10 files changed, 58 insertions(+), 15 deletions(-) create mode 100644 src/Cuemon.AspNetCore.Authentication/Properties/PackageReleaseNotes.txt diff --git a/src/Cuemon.AspNetCore.Authentication/Properties/PackageReleaseNotes.txt b/src/Cuemon.AspNetCore.Authentication/Properties/PackageReleaseNotes.txt new file mode 100644 index 000000000..61c98ba14 --- /dev/null +++ b/src/Cuemon.AspNetCore.Authentication/Properties/PackageReleaseNotes.txt @@ -0,0 +1,37 @@ +Version: 6.0.0 +Availability: NET Standard 2.0, NET Core 3.0, NET 5.0 +  +# Upgrade Steps +- Any former extension methods of the Cuemon.AspNetCore.Authentication namespace was merged into the Cuemon.Extensions.AspNetCore.Authentication namespace +  +# Breaking Changes +- RENAMED AuthenticationUtility class in the Cuemon.AspNetCore.Authentication namespace to Authenticator and removed all constants +- REMOVED DigestAccessAuthenticationParameters class from the Cuemon.AspNetCore.Authentication namespace +- REMOVED DigestAuthenticationUtility class from the Cuemon.AspNetCore.Authentication namespace +- REMOVED HmacAuthenticationParameters class from the Cuemon.AspNetCore.Authentication namespace +- MOVED BasicAuthenticationMiddleware class from the Cuemon.AspNetCore.Authentication namespace to Cuemon.AspNetCore.Authentication.Basic namespace (including refactoring) +- MOVED BasicAuthenticationOptions class from the Cuemon.AspNetCore.Authentication namespace to Cuemon.AspNetCore.Authentication.Basic namespace +- MOVED BasicAuthenticator delegate from the Cuemon.AspNetCore.Authentication namespace to Cuemon.AspNetCore.Authentication.Basic namespace +- MOVED DigestAccessAuthenticationMiddleware class from the Cuemon.AspNetCore.Authentication namespace to Cuemon.AspNetCore.Authentication.Digest namespace (including refactoring) +- MOVED DigestAccessAuthenticationOptions class from the Cuemon.AspNetCore.Authentication namespace to Cuemon.AspNetCore.Authentication.Digest namespace (including refactoring) +- MOVED DigestAccessAuthenticator delegate from the Cuemon.AspNetCore.Authentication namespace to Cuemon.AspNetCore.Authentication.Digest namespace +- RENAMED DigestAccessAuthenticationMiddleware class in the Cuemon.AspNetCore.Authentication.Digest namespace to DigestAuthenticationMiddleware +- RENAMED DigestAccessAuthenticationOptions class in the Cuemon.AspNetCore.Authentication.Digest namespace to DigestAuthenticationOptions +- RENAMED DigestAccessAuthenticator class in the Cuemon.AspNetCore.Authentication.Digest namespace to DigestAuthenticator +- MOVED HmacAuthenticationMiddleware class from the Cuemon.AspNetCore.Authentication namespace to Cuemon.AspNetCore.Authentication.Hmac namespace (including refactoring) +- MOVED HmacAuthenticationOptions class from the Cuemon.AspNetCore.Authentication namespace to Cuemon.AspNetCore.Authentication.Hmac namespace (including refactoring) +- MOVED HmacAuthenticator delegate from the Cuemon.AspNetCore.Authentication namespace to Cuemon.AspNetCore.Authentication.Hmac namespace +  +# New Features +- ADDED BasicAuthorizationHeader class in the Cuemon.AspNetCore.Authentication.Basic namespace that provides a representation of a HTTP Basic Authentication header +- ADDED BasicAuthorizationHeaderBuilder class in the Cuemon.AspNetCore.Authentication.Basic namespace that provides a way to fluently represent a HTTP Basic Authentication header +- ADDED BasicFields class in the Cuemon.AspNetCore.Authentication.Basic namespace that holds a collection of constants for BasicAuthorizationHeaderBuilder +- ADDED DigestAuthorizationHeader class in the Cuemon.AspNetCore.Authentication.Basic namespace that provides a representation of a HTTP Digest Access Authentication header +- ADDED DigestAuthorizationHeaderBuilder class in the Cuemon.AspNetCore.Authentication.Basic namespace that provides a way to fluently represent a HTTP Digest Access Authentication header +- ADDED DigestFields class in the Cuemon.AspNetCore.Authentication.Basic namespace that holds a collection of constants for DigestAuthorizationHeaderBuilder +- ADDED AuthorizationHeader class in the Cuemon.AspNetCore.Authentication namespace that represents the base class from which all implementations of authorization header should derive +- ADDED AuthorizationHeaderBuilder class in the Cuemon.AspNetCore.Authentication namespace that represents the base class from which all implementations of authorization header builders should derive +- ADDED AuthorizationHeaderOptions class in the Cuemon.AspNetCore.Authentication namespace that specifies options related to AuthorizationHeader +- ADDED INonceTracker interface in the Cuemon.AspNetCore.Authentication namespace that represents tracking of server-generated nonce values +- ADDED MemoryNonceTracker class in the Cuemon.AspNetCore.Authentication namespace that provides a default in-memory implementation of the INonceTracker interface +  \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj index 211110988..12c9fe0ca 100644 --- a/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj +++ b/src/Cuemon.AspNetCore.Mvc/Cuemon.AspNetCore.Mvc.csproj @@ -21,8 +21,4 @@ - - - - \ No newline at end of file diff --git a/src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt b/src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt index 58d3de014..b9612900c 100644 --- a/src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt @@ -1,12 +1,12 @@ Version: 6.0.0 -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, NET 5.0   # Upgrade Steps -- Any former extension methods of the Cuemon.AspNetCore.Mvc namespace was merged into the Cuemon.Extensions.AspNetCore.Mvc namespace +- Any former extension methods of the Cuemon.AspNetCore.Mvc namespace was merged either into the Cuemon.Extensions.AspNetCore.Mvc namespace or Cuemon.Extensions.AspNetCore namespace   # Breaking Changes - MOVED ICacheBusting interface (and related) from the Cuemon.AspNetCore.Mvc.Configuration namespace to Cuemon.AspNetCore.Configuration namespace -- MOVED AssemblyCacheBusting class (and related) from the Cuemon.AspNetCore.Mvc.Configuration namespace to Cuemon.Extensions.AspNetCore.Mvc.Configuration namespace +- MOVED AssemblyCacheBusting class (and related) from the Cuemon.AspNetCore.Mvc.Configuration namespace to Cuemon.Extensions.AspNetCore.Configuration namespace - MOVED ICacheableObjectResult interface (and related) from the Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace to Cuemon.AspNetCore.Mvc namespace - RENAMED HttpEntityTagHeader class in the Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace to HttpEntityTagHeaderFilter - RENAMED HttpLastModifiedHeader class in the Cuemon.AspNetCore.Mvc.Filters.Cacheable namespace to HttpLastModifiedHeaderFilter @@ -17,6 +17,7 @@ Availability: NET Standard 2.0, NET Core 3.0 # New Features - ADDED TooManyRequestsObjectResult class in the Cuemon.AspNetCore.Mvc namespace that is an ObjectResult that when executed will produce a Too Many Requests (429) response - ADDED TooManyRequestsResult class in the Cuemon.AspNetCore.Mvc namespace that is an ActionResult that returns a TooManyRequests (429) response +- ADDED CacheableObjectFactory class in the Cuemon.AspNetCore.Mvc namespace that provides access to factory methods for creating and configuring objects implementing the ICacheableObjectResult interface   # Improvements - COMPATIBLE with the changes applied to NET Core 3 in regards to only allowing asynchronous I/O diff --git a/src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt b/src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt index 3a70079d0..e74706340 100644 --- a/src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.AspNetCore/Properties/PackageReleaseNotes.txt @@ -1,14 +1,23 @@ Version: 6.0.0 -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, NET 5.0   # Upgrade Steps - Any former extension methods of the Cuemon.AspNetCore namespace was merged into the Cuemon.Extensions.AspNetCore namespace   # Breaking Changes - MOVED HttpExceptionDescriptor class from the Cuemon.AspNetCore.Http namespace to Cuemon.AspNetCore.Diagnostics namespace +- REMOVED ThrottlingRetryAfterHeader enum from the Cuemon.AspNetCore.Http.Throttling namespace +- CHANGED ThrottlingSentinelOptions class in the Cuemon.AspNetCore.Http.Throttling namespace to be more compliant with industry standards +- RENAMED ApplicationBuilderFactory class in the Cuemon.AspNetCore.Builder namespace to MiddlewareBuilderFactory   # New Features - ADDED IServerTiming interface in the Cuemon.AspNetCore.Diagnostics namespace that represents the Server Timing as per W3C Working Draft 28 July 2020 (https://www.w3.org/TR/2020/WD-server-timing-20200728/) - ADDED ServerTiming class in the Cuemon.AspNetCore.Diagnostics namespace that provides a default implementation of the IServerTiming interface - ADDED ServerTimingMetric class in the Cuemon.AspNetCore.Diagnostics namespace that represents a HTTP Server-Timing header field entry to communicate one metric and description for the given request-response cycle -- ADDED RetryConditionScope enum in the Cuemon.AspNetCore.Http.Headers namespace that specifies a set of values defining what value to use with a given HTTP header in regards to a retry condition \ No newline at end of file +- ADDED RetryConditionScope enum in the Cuemon.AspNetCore.Http.Headers namespace that specifies a set of values defining what value to use with a given HTTP header in regards to a retry condition +- ADDED CacheBusting class in the Cuemon.AspNetCore.Configuration namespace that represents a way to provide cache-busting capabilities +- ADDED CacheBustingOptions class in the Cuemon.AspNetCore.Configuration namespace that specifies options related to CacheBusting +- ADDED DynamicCacheBusting class in the Cuemon.AspNetCore.Configuration namespace that provides cache-busting capabilities on a duration based interval +- ADDED DynamicCacheBustingOptions class in the Cuemon.AspNetCore.Configuration namespace that specifies options related to DynamicCacheBusting +- ADDED ICacheBusting interface in the Cuemon.AspNetCore.Configuration namespace that is an interface to provide cache-busting capabilities +  \ No newline at end of file diff --git a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt index 920b79924..dbaee21a1 100644 --- a/src/Cuemon.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Core/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Upgrade Steps - To use the earlier built-in support for transient fault handling, please refer to the Cuemon.Resilience namespace, as it has been merged and refactored out of this assembly diff --git a/src/Cuemon.Resilience/Properties/PackageReleaseNotes.txt b/src/Cuemon.Resilience/Properties/PackageReleaseNotes.txt index 0456ef2fc..216f9f3cb 100644 --- a/src/Cuemon.Resilience/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Resilience/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED LatencyException class in the Cuemon.Resilience namespace that represents the exception that is thrown when a latency related operation was taking to long to complete diff --git a/src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt b/src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt index fc536eab9..09c2639bd 100644 --- a/src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Runtime.Caching/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Upgrade Steps - Some features (such as Memoization techniques and GetOrAdd convenience) was moved to the Cuemon.Extensions.Runtime.Caching namespace as extension methods (to keep the ICacheEnumerable{TKey} slim) diff --git a/src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt b/src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt index 246acbe17..d25ffd286 100644 --- a/src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Security.Cryptography/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Upgrade Steps - The Cuemon.Security assembly was removed with this version diff --git a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt index 91562d727..6cb177e43 100644 --- a/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Threading/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Upgrade Steps - The extent of refactoring applied to this project resulted in so many breaking changes that a git diff is advisable diff --git a/src/Cuemon.Xml/Properties/PackageReleaseNotes.txt b/src/Cuemon.Xml/Properties/PackageReleaseNotes.txt index 6678fc4bf..47d6780b6 100644 --- a/src/Cuemon.Xml/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Xml/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Upgrade Steps - The Cuemon.Serialization.Xml assembly and namespace was removed with this version From f0614fa2ce43a036edbc6b3d351c02c059eca7c6 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 27 Feb 2021 02:11:11 +0100 Subject: [PATCH 380/385] Renamed for clarity. --- .../ServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs b/src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs index ea3085123..cc23303e4 100644 --- a/src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs +++ b/src/Cuemon.Extensions.AspNetCore.Authentication/ServiceCollectionExtensions.cs @@ -13,7 +13,7 @@ public static class ServiceCollectionExtensions /// /// The to add services to. /// An that can be used to further configure other services. - public static IServiceCollection AddDigestAccessAuthenticationNonceTracker(this IServiceCollection services) + public static IServiceCollection AddInMemoryDigestAuthenticationNonceTracker(this IServiceCollection services) { Validator.ThrowIfNull(services, nameof(services)); services.AddSingleton(); From 1cfb08fe163d8a6714a0926c384234da37fde782 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 27 Feb 2021 02:53:45 +0100 Subject: [PATCH 381/385] Updated with .NET 5.0 --- src/Cuemon.Data.Integrity/Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Data.SqlClient/Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Data/Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Diagnostics/Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Extensions.Data/Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Extensions.IO/Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Extensions.Net/Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Extensions.Text/Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- .../Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.IO/Properties/PackageReleaseNotes.txt | 2 +- src/Cuemon.Net/Properties/PackageReleaseNotes.txt | 2 +- 30 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/Cuemon.Data.Integrity/Properties/PackageReleaseNotes.txt b/src/Cuemon.Data.Integrity/Properties/PackageReleaseNotes.txt index e9721db55..0f8e1e940 100644 --- a/src/Cuemon.Data.Integrity/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Data.Integrity/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Upgrade Steps - The Cuemon.Integrity namespace was removed with this version diff --git a/src/Cuemon.Data.SqlClient/Properties/PackageReleaseNotes.txt b/src/Cuemon.Data.SqlClient/Properties/PackageReleaseNotes.txt index d083e92b9..c30ad7d2a 100644 --- a/src/Cuemon.Data.SqlClient/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Data.SqlClient/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Breaking Changes - CHANGED SqlDataManager class in the Cuemon.Data.SqlClient namespace to be less dependant on base class and applied quality gate actions diff --git a/src/Cuemon.Data/Properties/PackageReleaseNotes.txt b/src/Cuemon.Data/Properties/PackageReleaseNotes.txt index d10352983..6de46a2eb 100644 --- a/src/Cuemon.Data/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Data/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Upgrade Steps - The Cuemon.Data.XmlClient assembly and namespace was removed with this version diff --git a/src/Cuemon.Diagnostics/Properties/PackageReleaseNotes.txt b/src/Cuemon.Diagnostics/Properties/PackageReleaseNotes.txt index ee56cc28e..43c958c62 100644 --- a/src/Cuemon.Diagnostics/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Diagnostics/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Breaking Changes - REMOVED EventLogEntryType enum from the Cuemon.Diagnostics namespace as it is now (finally) part of .NET Platform Extensions and .NET Core diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Properties/PackageReleaseNotes.txt index be4e71e5d..ca7cb9e9f 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, NET 5.0   # Upgrade Steps - The Cuemon.AspNetCore.Mvc.Formatters.Json namespace was removed with this version diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Properties/PackageReleaseNotes.txt index 02634bd3e..eeb529b0a 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, NET 5.0   # Upgrade Steps - The Cuemon.AspNetCore.Mvc.Formatters.Xml namespace was removed with this version diff --git a/src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt index 01ac1d0a0..7120475f1 100644 --- a/src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, NET 5.0   # Breaking Changes - RENAMED ToCacheableObjectResult{T} --> MakeCacheable{T} on the CacheableObjectResultExtensions class in the Cuemon.Extensions.AspNetCore.Mvc namespace (also included a non-generic variant: MakeCacheable) diff --git a/src/Cuemon.Extensions.AspNetCore/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.AspNetCore/Properties/PackageReleaseNotes.txt index 641c7cccf..ec0962c3b 100644 --- a/src/Cuemon.Extensions.AspNetCore/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.AspNetCore/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, NET 5.0   # Upgrade Steps - HttpResponseMessageExtensions class was not merged to this assembly diff --git a/src/Cuemon.Extensions.Collections.Generic/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Collections.Generic/Properties/PackageReleaseNotes.txt index 3880e193e..9ec26be35 100644 --- a/src/Cuemon.Extensions.Collections.Generic/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Collections.Generic/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED CollectionExtensions class in the Cuemon.Extensions.Collections.Generic namespace that consist of extension methods for the ICollection{T} interface: ToPartitioner{T}, AddRange{T} diff --git a/src/Cuemon.Extensions.Collections.Specialized/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Collections.Specialized/Properties/PackageReleaseNotes.txt index f47c06e8f..1cfc9a51f 100644 --- a/src/Cuemon.Extensions.Collections.Specialized/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Collections.Specialized/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED DictionaryExtensions class in the Cuemon.Extensions.Collections.Specialized namespace that consist of extension methods for the IDictionary{string, string[]} interface: ToNameValueCollection diff --git a/src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt index 57c9db934..8dd068359 100644 --- a/src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Core/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED ActionExtensions class in the Cuemon.Extensions namespace that consist of extension methods for the Action delegate: Configure{TOptions}, CreateInstance{T} diff --git a/src/Cuemon.Extensions.Data.Integrity/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Data.Integrity/Properties/PackageReleaseNotes.txt index f1edc1978..4d092ab8c 100644 --- a/src/Cuemon.Extensions.Data.Integrity/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Data.Integrity/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED AssemblyExtensions class in the Cuemon.Extensions.Data.Integrity namespace that consist of extension methods for the Assembly class: GetCacheValidator diff --git a/src/Cuemon.Extensions.Data/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Data/Properties/PackageReleaseNotes.txt index f95e18ddf..fa83bbf7c 100644 --- a/src/Cuemon.Extensions.Data/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Data/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED DataTransferExtensions class in the Cuemon.Extensions.Data namespace that consist of extension methods for the IDataReader interface: ToColumns, ToRows diff --git a/src/Cuemon.Extensions.DependencyInjection/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.DependencyInjection/Properties/PackageReleaseNotes.txt index f13db3b13..3129c1978 100644 --- a/src/Cuemon.Extensions.DependencyInjection/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.DependencyInjection/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED ServiceCollectionExtensions class in the Cuemon.Extensions.DependencyInjection namespace that consist of extension methods for the IServiceCollection interface: Add, Add{TOptions}, Add{TService, TImplementation}, Add{TService, TImplementation, TOptions}, TryAdd, TryAdd{TOptions}, TryAdd{TService, TImplementation}, TryAdd{TService, TImplementation, TOptions} diff --git a/src/Cuemon.Extensions.Diagnostics/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Diagnostics/Properties/PackageReleaseNotes.txt index 76841d8ef..59d238596 100644 --- a/src/Cuemon.Extensions.Diagnostics/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Diagnostics/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED ExceptionDescriptorExtensions class in the Cuemon.Extensions.Diagnostics namespace that consist of extension methods for the ExceptionDescriptor class: ToInsightsString diff --git a/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt index 7110fc924..53e029c08 100644 --- a/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Hosting/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, NET 5.0   # New Features - Added extension methods for IHostEnvironment: IsLocalDevelopment and IsNonProduction diff --git a/src/Cuemon.Extensions.IO/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.IO/Properties/PackageReleaseNotes.txt index 3332e0bb7..0650ff211 100644 --- a/src/Cuemon.Extensions.IO/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.IO/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0, NET Standard 2.1 +Availability: NET Standard 2.0, NET Standard 2.1, NET 5.0   # New Features - ADDED ByteArrayExtensions class in the Cuemon.Extensions.IO namespace that consist of extension methods for the byte[] struct: ToStream, ToStreamAsync diff --git a/src/Cuemon.Extensions.Net/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Net/Properties/PackageReleaseNotes.txt index a6fb93889..f4e18a0c7 100644 --- a/src/Cuemon.Extensions.Net/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Net/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED HttpManagerFactory class in the Cuemon.Extensions.Net.Http namespace that provides access to factory methods for creating and configuring HttpManager instances diff --git a/src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt index 9db989ecd..ac0df4f30 100644 --- a/src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Newtonsoft.Json/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Upgrade Steps - The Cuemon.Serialization.Json namespace was removed with this version diff --git a/src/Cuemon.Extensions.Reflection/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Reflection/Properties/PackageReleaseNotes.txt index a24eb3c74..686af98f6 100644 --- a/src/Cuemon.Extensions.Reflection/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Reflection/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED AssemblyExtensions class in the Cuemon.Extensions.Reflection namespace that consist of extension methods for the Assembly class: GetAssemblyVersion, GetFileVersion, GetProductVersion, IsDebugBuild diff --git a/src/Cuemon.Extensions.Runtime.Caching/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Runtime.Caching/Properties/PackageReleaseNotes.txt index 3805f65e4..a81acd5e0 100644 --- a/src/Cuemon.Extensions.Runtime.Caching/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Runtime.Caching/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED CacheEnumerableExtensions class in the Cuemon.Extensions.Runtime.Caching namespace that consist of extension methods for the ICacheEnumerable{TKey} interface: GetOrAdd, Memoize \ No newline at end of file diff --git a/src/Cuemon.Extensions.Text/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Text/Properties/PackageReleaseNotes.txt index 97742e289..5558e3cf6 100644 --- a/src/Cuemon.Extensions.Text/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Text/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED EncodingOptionsExtensions class in the Cuemon.Extensions.Text namespace that consist of extension methods for the EncodingOptionsExtensions class: DetectUnicodeEncoding diff --git a/src/Cuemon.Extensions.Threading/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Threading/Properties/PackageReleaseNotes.txt index 774c16ea8..910f8c3a0 100644 --- a/src/Cuemon.Extensions.Threading/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Threading/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED TaskExtensions class in the Cuemon.Extensions.Threading.Tasks namespace that consist of extension methods for the Task class: ContinueWithCapturedContext, ContinueWithCapturedContext{TResult}, ContinueWithSuppressedContext, ContinueWithSuppressedContext{TResult} diff --git a/src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt index 7b23fff2e..19c144274 100644 --- a/src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Xml/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Breaking Changes - RENAMED Copy --> ToStream on the XmlReaderExtensions class in the Cuemon.Xml namespace (also removed generic type parameters) diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt index 45c106b06..cae6f5bd9 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Core 3.1 +Availability: NET Core 3.1, NET 5.0   # New Features - ADDED IMvcFilterTest interface in the Cuemon.Extensions.Xunit.Hosting.AspNetCore.Mvc namespace that represents the members needed for ASP.NET Core MVC filter testing diff --git a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/PackageReleaseNotes.txt index 3344ffc50..a7641d7d5 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Xunit.Hosting.AspNetCore/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Core 3.1 +Availability: NET Core 3.1, NET 5.0   # New Features - ADDED FakeHttpResponseFeature class in the Cuemon.Extensions.Xunit.Hosting.AspNetCore.Http.Features namespace that represents a way to trigger IHttpResponseFeature.OnStarting diff --git a/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt index 20cb28c1c..4e453d52d 100644 --- a/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Xunit.Hosting/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0, NET Core 3.0 +Availability: NET Standard 2.0, NET Core 3.0, NET 5.0   # New Features - ADDED HostTest class in the Cuemon.Extensions.Xunit.Hosting namespace that represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection, should derive diff --git a/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt b/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt index 05ca34de5..66cd50f63 100644 --- a/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Extensions.Xunit/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # New Features - ADDED Test class in the Cuemon.Extensions.Xunit namespace that represents the base class from which all implementations of unit testing should derive \ No newline at end of file diff --git a/src/Cuemon.IO/Properties/PackageReleaseNotes.txt b/src/Cuemon.IO/Properties/PackageReleaseNotes.txt index b8e43a880..64d2dcd4f 100644 --- a/src/Cuemon.IO/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.IO/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0, NET Standard 2.1 +Availability: NET Standard 2.0, NET Standard 2.1, NET 5.0   # Upgrade Steps - The Cuemon.IO.Compression namespace was removed with this version diff --git a/src/Cuemon.Net/Properties/PackageReleaseNotes.txt b/src/Cuemon.Net/Properties/PackageReleaseNotes.txt index 48469e631..49b1131e5 100644 --- a/src/Cuemon.Net/Properties/PackageReleaseNotes.txt +++ b/src/Cuemon.Net/Properties/PackageReleaseNotes.txt @@ -1,5 +1,5 @@ Version: 6.0.0 -Availability: NET Standard 2.0 +Availability: NET Standard 2.0, NET 5.0   # Upgrade Steps - The Cuemon.Net.Mail assembly was removed with this version From f132cfd0c1afc2b4386eb8d7ef1b5dcb8e0631ee Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 27 Feb 2021 02:54:26 +0100 Subject: [PATCH 382/385] Moved publish from CI to CD. --- azure-pipelines.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f6ae5d8eb..b75c1894d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -228,13 +228,4 @@ jobs: inputs: PathtoPublish: $(Build.ArtifactStagingDirectory) ArtifactName: Packages - publishLocation: Container - - - task: NuGetCommand@2 - condition: eq(variables['Agent.OS'], 'Windows_NT') - displayName: 'Publish NuGet Packages to https://nuget.cuemon.net/v3/index.json' - inputs: - command: 'push' - packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg' - nuGetFeedType: 'external' - publishFeedCredentials: 'Cuemon-Nuget' + publishLocation: Container \ No newline at end of file From a60c6b36033649c71ba6eef195bf0ce22d4eaaa2 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 27 Feb 2021 02:54:49 +0100 Subject: [PATCH 383/385] Changed author and updated logo. --- Directory.Build.props | 4 ++-- README.md | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index cfb2628d2..fd17da7f3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -15,10 +15,10 @@ Copyright © Geekle 2009-2021. All rights reserved. - Michael Mortensen + gimlichael Geekle Cuemon for .NET - https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png + https://nblcdn.net/cuemon.net/128x128.png https://www.cuemon.net/ MIT https://github.com/gimlichael/Cuemon diff --git a/README.md b/README.md index 51ea7be6b..9d5cd658d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Cuemon for .NET](https://nblcdn.net/themes/cuemon.net/img/core/128x128x.png) +![Cuemon for .NET](https://nblcdn.net/cuemon/128x128.png) # Cuemon for .NET @@ -8,9 +8,9 @@ It is, by heart, free, flexible and built to extend and boost your agile codebel ## State of the Union -Cuemon for .NET (formerly Cuemon .NET Standard) has been completely refactored and updated to support .NET Core 3.1 while receiving a name that reflects the forthcoming version of .NET - .NET 5. +Cuemon for .NET (formerly Cuemon .NET Standard) has been completely refactored and updated to support .NET 5 while for the most part being compatible with .NET Standard 2.0, .NET Core 2.0 and .NET Core 3.0. -Another big change for this upcoming release is the versioning; the world has spoken - and chosen semantic versioning. +Another big change for this upcoming release is the versioning; the developers has spoken and cast their love on semantic versioning. The release for now is planned to be 6.0.0. Check out the WIP documentation (generated by DocFx): https://docs.cuemon.net/ @@ -19,7 +19,7 @@ All CI and CD integrations are done on [Microsoft Azure DevOps](https://azure.mi All code quality analysis are done by [SonarCloud](https://sonarcloud.io/) and [CodeCov.io](https://codecov.io/). -Stay tuned for more exiting news! +Currently work is done on ironing out the kinks in relations to NuGet package description, release notes conventions, concepts and last minute refactorings to provide the best experience possible with RC-1! ![License](https://img.shields.io/github/license/gimlichael/cuemon) [![Build Status](https://dev.azure.com/gimlichael/Cuemon/_apis/build/status/gimlichael.Cuemon?branchName=development)](https://dev.azure.com/gimlichael/Cuemon/_build/latest?definitionId=9&branchName=development) [![codecov](https://codecov.io/gh/gimlichael/Cuemon/branch/development/graph/badge.svg)](https://codecov.io/gh/gimlichael/Cuemon) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Cuemon&metric=coverage)](https://sonarcloud.io/dashboard?id=Cuemon) From 7a99923e42ef193a9d1a1119fcbe09a399177706 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 27 Feb 2021 06:07:30 +0100 Subject: [PATCH 384/385] Removed attributes from Project element. --- Directory.Build.props | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index fd17da7f3..80efa677d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,4 @@ - - + $(MSBuildProjectName.EndsWith('Tests')) From 02bbadf2e6dc9c94ad1596ddce069b7ad2838706 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Sat, 27 Feb 2021 06:10:25 +0100 Subject: [PATCH 385/385] Removed attributes from Project element. --- Directory.Build.props | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index fd17da7f3..14bf81c82 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,4 @@ - - + $(MSBuildProjectName.EndsWith('Tests')) @@ -72,4 +71,4 @@ - \ No newline at end of file +