From 2f0bb21afc19ee39df424a287d2f889506b03b32 Mon Sep 17 00:00:00 2001
From: Paulo Morgado <470455+paulomorgado@users.noreply.github.com>
Date: Wed, 15 Nov 2023 11:33:27 +0000
Subject: [PATCH] Replaced LINQ with iteration for performance improvements.

---
 .../Client.Class.ConvertToString.liquid       | 13 ++++-
 .../Client.Class.PathParameter.liquid         | 18 +++++--
 .../Templates/Client.Class.liquid             |  4 +-
 .../Templates/JsonExceptionConverter.liquid   | 48 +++++++++++++------
 ...ork=net7.0_generatesCode=True.verified.txt | 38 ++++++++++++---
 .../GeneratedClientsCs.gen                    | 38 ++++++++++++---
 src/NSwag.sln                                 |  6 +++
 src/NuGet.Config                              |  4 +-
 8 files changed, 133 insertions(+), 36 deletions(-)

diff --git a/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.ConvertToString.liquid b/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.ConvertToString.liquid
index 08f426cfbd..4d52676038 100644
--- a/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.ConvertToString.liquid
+++ b/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.ConvertToString.liquid
@@ -37,10 +37,19 @@ private string ConvertToString(object value, System.Globalization.CultureInfo cu
     {
         return System.Convert.ToBase64String((byte[]) value);
     }
+    else if (value is string[])
+    {
+        return string.Join(",", (string[])value);
+    }
     else if (value.GetType().IsArray)
     {
-        var array = System.Linq.Enumerable.OfType<object>((System.Array) value);
-        return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
+        var valueArray = (System.Array)value;
+        var valueTextArray = new string[valueArray.Length];
+        for (var i = 0; i < valueArray.Length; i++)
+        {
+            valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo);
+        }
+        return string.Join(",", valueTextArray);
     }
 
     var result = System.Convert.ToString(value, cultureInfo);
diff --git a/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.PathParameter.liquid b/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.PathParameter.liquid
index afbecb8a5e..7132ad9769 100644
--- a/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.PathParameter.liquid
+++ b/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.PathParameter.liquid
@@ -1,13 +1,25 @@
 {% if parameter.IsDateTimeArray -%}
-urlBuilder_.Append(System.Uri.EscapeDataString(string.Join(",", System.Linq.Enumerable.Select({{ parameter.VariableName }}, s_ => s_.ToString("{{ ParameterDateTimeFormat }}", System.Globalization.CultureInfo.InvariantCulture)))));
+for (var i = 0; i < {{ parameter.VariableName }}.Length; i++)
+{
+    if (i > 0) urlBuilder_.Append(',');
+    urlBuilder_.Append(System.Uri.EscapeDataString({{ parameter.VariableName }}[i].ToString("{{ ParameterDateTimeFormat }}", System.Globalization.CultureInfo.InvariantCulture)));
+}
 {% elsif parameter.IsDateArray -%}
-urlBuilder_.Append(System.Uri.EscapeDataString(string.Join(",", System.Linq.Enumerable.Select({{ parameter.VariableName }}, s_ => s_.ToString("{{ ParameterDateFormat }}", System.Globalization.CultureInfo.InvariantCulture)))));
+for (var i = 0; i < {{ parameter.VariableName }}.Length; i++)
+{
+    if (i > 0) urlBuilder_.Append(',');
+    urlBuilder_.Append(System.Uri.EscapeDataString({{ parameter.VariableName }}[i].ToString("{{ ParameterDateFormat }}", System.Globalization.CultureInfo.InvariantCulture)));
+}
 {% elsif parameter.IsDateTime -%}
 urlBuilder_.Append(System.Uri.EscapeDataString({{ parameter.VariableName }}.ToString("{{ ParameterDateTimeFormat }}", System.Globalization.CultureInfo.InvariantCulture)));
 {% elsif parameter.IsDate -%}
 urlBuilder_.Append(System.Uri.EscapeDataString({{ parameter.VariableName }}.ToString("{{ ParameterDateFormat }}", System.Globalization.CultureInfo.InvariantCulture)));
 {% elsif parameter.IsArray -%}
-urlBuilder_.Append(System.Uri.EscapeDataString(string.Join(",", System.Linq.Enumerable.Select({{ parameter.VariableName }}, s_ => ConvertToString(s_, System.Globalization.CultureInfo.InvariantCulture)))));
+for (var i = 0; i < {{ parameter.VariableName }}.Length; i++)
+{
+    if (i > 0) urlBuilder_.Append(',');
+    urlBuilder_.Append(ConvertToString({{ parameter.VariableName }}[i], System.Globalization.CultureInfo.InvariantCulture));
+}
 {% else -%}
 urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString({{ parameter.VariableName }}, System.Globalization.CultureInfo.InvariantCulture)));
 {%- endif %}
diff --git a/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.liquid b/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.liquid
index 1666ea275b..8397ed8bef 100644
--- a/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.liquid
+++ b/src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.liquid
@@ -333,7 +333,9 @@
                 var disposeResponse_ = true;
                 try
                 {
-                    var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+                    var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
+                    foreach (var item_ in response_.Headers)
+                        headers_[item_.Key] = item_.Value;
                     if (response_.Content != null && response_.Content.Headers != null)
                     {
                         foreach (var item_ in response_.Content.Headers)
diff --git a/src/NSwag.CodeGeneration.CSharp/Templates/JsonExceptionConverter.liquid b/src/NSwag.CodeGeneration.CSharp/Templates/JsonExceptionConverter.liquid
index f0dfb80567..45942aafa3 100644
--- a/src/NSwag.CodeGeneration.CSharp/Templates/JsonExceptionConverter.liquid
+++ b/src/NSwag.CodeGeneration.CSharp/Templates/JsonExceptionConverter.liquid
@@ -126,27 +126,47 @@ internal class JsonExceptionConverter : Newtonsoft.Json.JsonConverter
     private System.Collections.Generic.IDictionary<System.Reflection.PropertyInfo, string> GetExceptionProperties(System.Type exceptionType)
     {
         var result = new System.Collections.Generic.Dictionary<System.Reflection.PropertyInfo, string>();
-        foreach (var property in System.Linq.Enumerable.Where(System.Reflection.RuntimeReflectionExtensions.GetRuntimeProperties(exceptionType), 
-            p => p.GetMethod?.IsPublic == true))
+        foreach (var property in System.Reflection.RuntimeReflectionExtensions.GetRuntimeProperties(exceptionType))
         {
+            if (property.GetMethod?.IsPublic != true)
+            {
+                continue;
+            }
+
             var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute<Newtonsoft.Json.JsonPropertyAttribute>(property);
             var propertyName = attribute != null ? attribute.PropertyName : property.Name;
-    
-            if (!System.Linq.Enumerable.Contains(new[] { "Message", "StackTrace", "Source", "InnerException", "Data", "TargetSite", "HelpLink", "HResult" }, propertyName))
-                result[property] = propertyName;
+
+            switch (propertyName)
+            {
+                case "Message":
+                case "StackTrace":
+                case "Source":
+                case "InnerException":
+                case "Data":
+                case "TargetSite":
+                case "HelpLink":
+                case "HResult":
+                    break;
+                default:
+                    result[property] = propertyName;
+                    break;
+            }
         }
         return result;
     }
     
-    private void SetExceptionFieldValue(Newtonsoft.Json.Linq.JObject jObject, string propertyName, object value, string fieldName, Newtonsoft.Json.Serialization.IContractResolver resolver, Newtonsoft.Json.JsonSerializer serializer)
-    {
-        var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(System.Exception)).GetDeclaredField(fieldName);
-        var jsonPropertyName = resolver is Newtonsoft.Json.Serialization.DefaultContractResolver ? ((Newtonsoft.Json.Serialization.DefaultContractResolver)resolver).GetResolvedPropertyName(propertyName) : propertyName;
-        var property = System.Linq.Enumerable.FirstOrDefault(jObject.Properties(), p => System.String.Equals(p.Name, jsonPropertyName, System.StringComparison.OrdinalIgnoreCase));
-        if (property != null)
+        private void SetExceptionFieldValue(Newtonsoft.Json.Linq.JObject jObject, string propertyName, object value, string fieldName, Newtonsoft.Json.Serialization.IContractResolver resolver, Newtonsoft.Json.JsonSerializer serializer)
         {
-            var fieldValue = property.Value.ToObject(field.FieldType, serializer);
-            field.SetValue(value, fieldValue);
+            var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(System.Exception)).GetDeclaredField(fieldName);
+            var jsonPropertyName = resolver is Newtonsoft.Json.Serialization.DefaultContractResolver ? ((Newtonsoft.Json.Serialization.DefaultContractResolver)resolver).GetResolvedPropertyName(propertyName) : propertyName;
+            foreach (var property in jObject.Properties())
+            {
+                if (System.String.Equals(p.Name, jsonPropertyName, System.StringComparison.OrdinalIgnoreCase))
+                {
+                    var fieldValue = property.Value.ToObject(field.FieldType, serializer);
+                    field.SetValue(value, fieldValue);
+                    break;
+                }
+            }
         }
     }
-}
\ No newline at end of file
diff --git a/src/NSwag.ConsoleCore.Tests/GenerateSampleSpecificationTests.CheckCSharpClientsAsync_projectName=NSwag.Sample.NET70Minimal_targetFramework=net7.0_generatesCode=True.verified.txt b/src/NSwag.ConsoleCore.Tests/GenerateSampleSpecificationTests.CheckCSharpClientsAsync_projectName=NSwag.Sample.NET70Minimal_targetFramework=net7.0_generatesCode=True.verified.txt
index 881d4e2b51..8e97fbdf8c 100644
--- a/src/NSwag.ConsoleCore.Tests/GenerateSampleSpecificationTests.CheckCSharpClientsAsync_projectName=NSwag.Sample.NET70Minimal_targetFramework=net7.0_generatesCode=True.verified.txt
+++ b/src/NSwag.ConsoleCore.Tests/GenerateSampleSpecificationTests.CheckCSharpClientsAsync_projectName=NSwag.Sample.NET70Minimal_targetFramework=net7.0_generatesCode=True.verified.txt
@@ -91,7 +91,9 @@ namespace MyNamespace
                     var disposeResponse_ = true;
                     try
                     {
-                        var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+                        var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
+                        foreach (var item_ in response_.Headers)
+                            headers_[item_.Key] = item_.Value;
                         if (response_.Content != null && response_.Content.Headers != null)
                         {
                             foreach (var item_ in response_.Content.Headers)
@@ -174,7 +176,9 @@ namespace MyNamespace
                     var disposeResponse_ = true;
                     try
                     {
-                        var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+                        var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
+                        foreach (var item_ in response_.Headers)
+                            headers_[item_.Key] = item_.Value;
                         if (response_.Content != null && response_.Content.Headers != null)
                         {
                             foreach (var item_ in response_.Content.Headers)
@@ -305,10 +309,19 @@ namespace MyNamespace
             {
                 return System.Convert.ToBase64String((byte[]) value);
             }
+            else if (value is string[])
+            {
+                return string.Join(",", (string[])value);
+            }
             else if (value.GetType().IsArray)
             {
-                var array = System.Linq.Enumerable.OfType<object>((System.Array) value);
-                return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
+                var valueArray = (System.Array)value;
+                var valueTextArray = new string[valueArray.Length];
+                for (var i = 0; i < valueArray.Length; i++)
+                {
+                    valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo));
+                }
+                return string.Join(",", valueTextArray);
             }
 
             var result = System.Convert.ToString(value, cultureInfo);
@@ -389,7 +402,9 @@ namespace MyNamespace
                     var disposeResponse_ = true;
                     try
                     {
-                        var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+                        var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
+                        foreach (var item_ in response_.Headers)
+                            headers_[item_.Key] = item_.Value;
                         if (response_.Content != null && response_.Content.Headers != null)
                         {
                             foreach (var item_ in response_.Content.Headers)
@@ -518,10 +533,19 @@ namespace MyNamespace
             {
                 return System.Convert.ToBase64String((byte[]) value);
             }
+            else if (value is string[])
+            {
+                return string.Join(",", (string[])value);
+            }
             else if (value.GetType().IsArray)
             {
-                var array = System.Linq.Enumerable.OfType<object>((System.Array) value);
-                return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
+                var valueArray = (System.Array)value;
+                var valueTextArray = new string[valueArray.Length];
+                for (var i = 0; i < valueArray.Length; i++)
+                {
+                    valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo));
+                }
+                return string.Join(",", valueTextArray);
             }
 
             var result = System.Convert.ToString(value, cultureInfo);
diff --git a/src/NSwag.Sample.NET70Minimal/GeneratedClientsCs.gen b/src/NSwag.Sample.NET70Minimal/GeneratedClientsCs.gen
index 3f53433a6a..b464f0ca78 100644
--- a/src/NSwag.Sample.NET70Minimal/GeneratedClientsCs.gen
+++ b/src/NSwag.Sample.NET70Minimal/GeneratedClientsCs.gen
@@ -91,7 +91,9 @@ namespace MyNamespace
                     var disposeResponse_ = true;
                     try
                     {
-                        var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+                        var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
+                        foreach (var item_ in response_.Headers)
+                            headers_[item_.Key] = item_.Value;
                         if (response_.Content != null && response_.Content.Headers != null)
                         {
                             foreach (var item_ in response_.Content.Headers)
@@ -174,7 +176,9 @@ namespace MyNamespace
                     var disposeResponse_ = true;
                     try
                     {
-                        var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+                        var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
+                        foreach (var item_ in response_.Headers)
+                            headers_[item_.Key] = item_.Value;
                         if (response_.Content != null && response_.Content.Headers != null)
                         {
                             foreach (var item_ in response_.Content.Headers)
@@ -305,10 +309,19 @@ namespace MyNamespace
             {
                 return System.Convert.ToBase64String((byte[]) value);
             }
+            else if (value is string[])
+            {
+                return string.Join(",", (string[])value);
+            }
             else if (value.GetType().IsArray)
             {
-                var array = System.Linq.Enumerable.OfType<object>((System.Array) value);
-                return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
+                var valueArray = (System.Array)value;
+                var valueTextArray = new string[valueArray.Length];
+                for (var i = 0; i < valueArray.Length; i++)
+                {
+                    valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo));
+                }
+                return string.Join(",", valueTextArray);
             }
 
             var result = System.Convert.ToString(value, cultureInfo);
@@ -389,7 +402,9 @@ namespace MyNamespace
                     var disposeResponse_ = true;
                     try
                     {
-                        var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
+                        var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
+                        foreach (var item_ in response_.Headers)
+                            headers_[item_.Key] = item_.Value;
                         if (response_.Content != null && response_.Content.Headers != null)
                         {
                             foreach (var item_ in response_.Content.Headers)
@@ -518,10 +533,19 @@ namespace MyNamespace
             {
                 return System.Convert.ToBase64String((byte[]) value);
             }
+            else if (value is string[])
+            {
+                return string.Join(",", (string[])value);
+            }
             else if (value.GetType().IsArray)
             {
-                var array = System.Linq.Enumerable.OfType<object>((System.Array) value);
-                return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
+                var valueArray = (System.Array)value;
+                var valueTextArray = new string[valueArray.Length];
+                for (var i = 0; i < valueArray.Length; i++)
+                {
+                    valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo));
+                }
+                return string.Join(",", valueTextArray);
             }
 
             var result = System.Convert.ToString(value, cultureInfo);
diff --git a/src/NSwag.sln b/src/NSwag.sln
index 5af5e15e45..78cc78694c 100644
--- a/src/NSwag.sln
+++ b/src/NSwag.sln
@@ -102,6 +102,7 @@ EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NSwag.ApiDescription.Client", "NSwag.ApiDescription.Client", "{E6E40935-0C79-480B-BF29-8C493AC7E518}"
 	ProjectSection(SolutionItems) = preProject
 		NSwag.ApiDescription.Client\NSwag.ApiDescription.Client.nuspec = NSwag.ApiDescription.Client\NSwag.ApiDescription.Client.nuspec
+		NSwag.ApiDescription.Client\NSwag.ApiDescription.Client.props = NSwag.ApiDescription.Client\NSwag.ApiDescription.Client.props
 		NSwag.ApiDescription.Client\NSwag.ApiDescription.Client.targets = NSwag.ApiDescription.Client\NSwag.ApiDescription.Client.targets
 	EndProjectSection
 EndProject
@@ -134,6 +135,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NSwag.Sample.NET80", "NSwag
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NSwag.Sample.NET80Minimal", "NSwag.Sample.NET80Minimal\NSwag.Sample.NET80Minimal.csproj", "{F0569608-BD55-4316-94F0-E85A14D7FE14}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FBF5AB11-68C3-41B8-9A08-5D25D6E814E4}"
+	ProjectSection(SolutionItems) = preProject
+		NuGet.Config = NuGet.Config
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
diff --git a/src/NuGet.Config b/src/NuGet.Config
index 0c74ec458f..44dedea914 100644
--- a/src/NuGet.Config
+++ b/src/NuGet.Config
@@ -2,8 +2,8 @@
 <configuration>
   <packageSources>
     <!--To inherit the global NuGet package sources remove the <clear/> line below -->
-    <clear />
+    <!--<clear />
     <add key="project" value="libs" />
-    <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
+    <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />-->
   </packageSources>
 </configuration>