azure-functions-openapi-extension icon indicating copy to clipboard operation
azure-functions-openapi-extension copied to clipboard

Stack overflow in System.Globalization.CompareInfo

Open johnholliday opened this issue 2 years ago • 3 comments

Describe the issue Creating a simple azure function produces a stack overflow exception when loading the swagger ui page.

To Reproduce Steps to reproduce the behavior:

  1. Create a new Azure Function App with a single function.
  2. Run in the debugger and click the generated Swagger UI page link.
  3. Swagger UI page fails to load => Stack Overflow Exception

Expected behavior Swagger UI page opens displaying the available function endpoint.

Code

public class ClientsCreate
{
    private ECMLDataContext Context { get; set; }
    private ILogger<ClientsCreate> Log { get; set; }

    public ClientsCreate(ILogger<ClientsCreate> log)
    {
        //Context = new ECMLDataContext();
        Log = log;
    }

    [OpenApiOperation(nameof(ClientsCreate), 
        tags: new[] { "Ecml" },
        Description = "Creates a new Client record")]
    [OpenApiRequestBody(
        contentType: "application/json",
        bodyType: typeof(Client),
        Required = true,
        Description = "The body of the request")]
    [OpenApiResponseWithBody(
        statusCode: HttpStatusCode.OK, 
        contentType: "application/json", 
        bodyType: typeof(string), 
        Description = "Indicates success and returns a user-friendly message")]
    [OpenApiResponseWithBody(
        HttpStatusCode.BadRequest, 
        contentType: "application/json", 
        bodyType: typeof(string), 
        Description = "Indicates failure and returns a user-friendly message")]
    [FunctionName(nameof(ClientsCreate))]
    public async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "clients")] HttpRequest req)
    {
        var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        var client = JsonConvert.DeserializeObject<Client>(requestBody);
        Context.Clients.Add(client);
        await Context.SaveChangesAsync();
        return new OkObjectResult(client);
    }
}

Environment (please complete the following information, if applicable):

  • OS: Windows 11
  • Browser Edge
  • Version 113.0.1774.35 (Official build) (64-bit)

Call Stack

 	[Managed to Native Transition]	
>	System.Private.CoreLib.dll!System.Globalization.CompareInfo.IcuCompareString(System.ReadOnlySpan<char> string1, System.ReadOnlySpan<char> string2, System.Globalization.CompareOptions options) Line 50	C#
 	System.Private.CoreLib.dll!System.Globalization.CompareInfo.Compare(System.ReadOnlySpan<char> string1, System.ReadOnlySpan<char> string2, System.Globalization.CompareOptions options) Line 448	C#
 	System.Private.CoreLib.dll!System.Globalization.CompareInfo.Compare(string string1, string string2, System.Globalization.CompareOptions options) Line 300	C#
 	System.Private.CoreLib.dll!string.Equals(string value, System.StringComparison comparisonType) Line 665	C#
 	Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions.TypeExtensions.IsArrayType(System.Type type)	Unknown
 	Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ObjectTypeVisitor.IsVisitable(System.Type type)	Unknown
 	Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.OpenApiSchemaAcceptor.Accept(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.VisitorCollection collection, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy)	Unknown
 	Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ObjectTypeVisitor.ProcessProperties(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.IOpenApiSchemaAcceptor instance, string schemaName, System.Collections.Generic.Dictionary<string, System.Reflection.PropertyInfo> properties, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy)	Unknown
 	Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ObjectTypeVisitor.Visit(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions.IAcceptor acceptor, System.Collections.Generic.KeyValuePair<string, System.Type> type, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy, System.Attribute[] attributes)	Unknown
 	Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.OpenApiSchemaAcceptor.Accept(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.VisitorCollection collection, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy)	Unknown

johnholliday avatar May 09 '23 01:05 johnholliday

Update This error occurs when using the OpenApiRequestBody attribute and specifying a custom object in the bodyType field, and the custom object has a property that references another class whose members include recursive references to the original object class.

        [OpenApiRequestBody(
            contentType: "application/json",
            bodyType: typeof(Client),
            Required = true,
            Description = "The body of the request")]

In this case, the Client class is declared as follows:

public partial class Client : CommonEntityBase
{
    public string? Name { get; set; }
    public string? Description { get; set; }
    public int ClientId { get; set; }
    public virtual Consultant Consultant { get; set; } = null!;

    partial void OnCreated();

    public Client():base()
    {
        Name = string.Empty;
        Description = string.Empty;
        OnCreated();
    }
}

Note the reference to the Consultant class, which may contain a collection of Client instances. This is a common one-to-many persistence pattern where a class (in this case Consultant) references a collection of Clients, each of which refers back to the parent instance.

	public partial class Consultant : CommonEntityBase
	{
		/// <summary>Method called from the constructor</summary>
		partial void OnCreated();

		/// <summary>Initializes a new instance of the <see cref="Consultant"/> class.</summary>
		public Consultant() : base()
		{
			this.Clients = new List<Client>();
			this.Company = string.Empty;
			this.Name = string.Empty;
			this.Title = string.Empty;
			OnCreated();
		}

		/// <summary>Gets or sets the Company field. </summary>
		public System.String Company { get; set; }
		/// <summary>Gets or sets the Id field. </summary>
		public System.Int32 Id { get; set; }
		/// <summary>Gets or sets the Name field. </summary>
		public System.String Name { get; set; }
		/// <summary>Gets or sets the Title field. </summary>
		public System.String Title { get; set; }
		/// <summary>Represents the navigator which is mapped onto the association 'Consultant.Clients - Client.Consultant (1:n)'</summary>
		public virtual List<Client> Clients { get; set; }
		/// <summary>Represents the navigator which is mapped onto the association 'Consultant.Profile - UserProfile.Consultant (1:1)'</summary>
		public virtual UserProfile Profile { get; set; } = null!;
	}

johnholliday avatar May 09 '23 02:05 johnholliday

@johnholliday, did you ever get this resolved? Running into something very similar. I have an Azure Functions project. Loads fine and I can execute requests via POSTMAN. The problem occurs when I load Swagger UI:

 Stack overflow.
[2023-06-16T12:57:52.251Z]    at Interop+Globalization.CompareString(IntPtr, Char*, Int32, Char*, Int32, System.Globalization.CompareOptions)
[2023-06-16T12:57:52.252Z]    at System.Globalization.CompareInfo.IcuCompareString(System.ReadOnlySpan`1<Char>, System.ReadOnlySpan`1<Char>, System.Globalization.CompareOptions)
[2023-06-16T12:57:52.255Z]    at System.Globalization.CompareInfo.Compare(System.ReadOnlySpan`1<Char>, System.ReadOnlySpan`1<Char>, System.Globalization.CompareOptions)
[2023-06-16T12:57:52.258Z]    at System.Globalization.CompareInfo.Compare(System.String, System.String, System.Globalization.CompareOptions)
[2023-06-16T12:57:52.264Z]    at System.String.Equals(System.String, System.StringComparison)
[2023-06-16T12:57:52.267Z]    at System.Linq.Enumerable+WhereArrayIterator`1[[System.__Canon, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
[2023-06-16T12:57:52.270Z]    at System.Linq.Enumerable.Any[[System.__Canon, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Collections.Generic.IEnumerable`1<System.__Canon>)
[2023-06-16T12:57:52.273Z]    at Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions.TypeExtensions.IsArrayType(System.Type)
[2023-06-16T12:57:52.275Z]    at Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.OpenApiSchemaAcceptor.Accept(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.VisitorCollection, Newtonsoft.Json.Serialization.NamingStrategy)
[2023-06-16T12:57:52.282Z]    at Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ObjectTypeVisitor.ProcessProperties(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.IOpenApiSchemaAcceptor, System.String, System.Collections.Generic.Dictionary`2<System.String,System.Reflection.PropertyInfo>, Newtonsoft.Json.Serialization.NamingStrategy)
[2023-06-16T12:57:52.285Z]    at Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ObjectTypeVisitor.Visit(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions.IAcceptor, System.Collections.Generic.KeyValuePair`2<System.String,System.Type>, Newtonsoft.Json.Serialization.NamingStrategy, System.Attribute[])

The last 3 lines repeat over and over:

  • OpenApiSchemaAcceptor.Accept
  • ObjectTypeVisitor.ProcessProperties
  • ObjectTypeVisitor.Visit

super-jb avatar Jun 16 '23 13:06 super-jb

I think that I have the exact same error.

a-wallen avatar May 14 '24 00:05 a-wallen