Multidimensional array parameter binding with ASP.NET Minimal API.

huangapple go评论73阅读模式
英文:

Multidimensional array parameter binding with ASP.NET Minimal API

问题

I have quite a few places where multidimensional arrays of varying lengths are used as query parameters.

With controllers, a request with /multi?vals[0][0]=5&vals[0][1]=6&vals[1][0]=6&vals[1][1]=7 binds without issues.

[Route("/multi")]
public class MultiController : Controller
{
    [HttpGet("")]
    public IActionResult Index([FromQuery] int[][] vals)
    {
        return this.Ok(string.Join(" ", vals.Select(x => string.Join(":", x))));
    }
}

With Minimal APIs,

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.Services.AddControllers();
        var app = builder.Build();

        app.MapGet("/multi-minimal", ([FromQuery] int[][] vals) => $"Hello World! {string.Join(":::", vals.Select(x => string.Join(":", x)))}");
        app.MapControllers();
        app.Run();
    }
}

There is an error trying to map the route

InvalidOperationException: No public static bool int[].TryParse(string, out int[]) method found for vals.
Microsoft.AspNetCore.Http.RequestDelegateFactory.BindParameterFromValue(ParameterInfo parameter, Expression valueExpression, RequestDelegateFactoryContext factoryContext, string source)

I don't see how it's possible to register a static TryParse method on an int[].

I have seen some documentation around BindAsync for scenarios like the following:

public class Dto
{
    [BindProperty(Name = "coordinates")]
    public int[][] Coordinates { get; set; }

    [BindProperty(Name = "a")]
    public string? PropA { get; set; }

    [BindProperty(Name = "b")]
    public string PropB { get; set; }

    // lots more properties

    public static ValueTask<Dto?> BindAsync(HttpContext context)
    {
        throw new NotImplementedException();
    }
}

But for larger DTOs, it's a tad inconvenient to have to parse the entire query string and set properties that are already supported. This seemed more targeted at 'complex types,' but in my case, it's more 'complex primitives' I am trying to bind.

Any suggestions on how to get app.MapGet("/multi-minimal", ([FromQuery] int[][] vals) => {}"); working with varying size multidimensional arrays would be greatly appreciated.

英文:

I have quite a few places where multidimensional arrays of varying lengths are used as query parameters.

With controllers a request with to /multi?vals[0][0]=5&vals[0][1]=6&vals[1][0]=6&vals[1][1]=7 binds without issues.

    [Route("/multi")]
    public class MultiController : Controller
    {
        [HttpGet("")]
        public IActionResult Index([FromQuery] int[][] vals)
        {
            return this.Ok(string.Join(" ", vals.Select(x => string.Join(":", x))));
        }
    }

With Minimal APIs,

 public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            builder.Services.AddControllers();
            var app = builder.Build();

            app.MapGet("/multi-minimal", ([FromQuery] int[][] vals) => $"Hello World! {string.Join(":::", vals.Select(x => string.Join(":", x)))}");
            app.MapControllers();
            app.Run();
        }
    }

There is an error trying to map the route

InvalidOperationException: No public static bool int[].TryParse(string, out int[]) method found for vals.
Microsoft.AspNetCore.Http.RequestDelegateFactory.BindParameterFromValue(ParameterInfo parameter, Expression valueExpression, RequestDelegateFactoryContext factoryContext, string source)

I don't see how its possible to register a static TryParse method on an int[].

I have seen some documentation around BindAsync for scenarios like the following:

    public class Dto
    {
        [BindProperty(Name = "coordinates")]
        public int[][] Coordinates { get; set; }

        [BindProperty(Name = "a")]
        public string? PropA { get; set; }

        [BindProperty(Name = "b")]
        public string PropB { get; set; }

	  // lots more properties

        public static ValueTask<Dto?> BindAsync(HttpContext context)
        {
            throw new NotImplementedException();
        }
    }

But for larger DTOs it's a tad inconvenient to have to parse the entire query string and set properties that are already supported. This seemed more targeted at 'complex types' but in my case its more 'complex primitives' I am trying to bind.

Any suggestions on how to get app.MapGet("/multi-minimal", ([FromQuery] int[][] vals) => {}"); working with varying size multidimensional arrays would be greatly appreciated.

答案1

得分: 1

First of all int[][] is not multidimensional array (that would be int[,]) but a jagged one.

我首先要指出的是 int[][] 不是多维数组(多维数组应该是 int[,]),而是交错数组

I would argue that this is pretty much covered in the docs:

我认为这在文档中已经相当详细地涵盖了:

Binding query strings or header values to an array of complex types is supported when the type has TryParse implemented.

当类型实现了 TryParse 时,将查询字符串或头部值绑定到复杂类型数组是受支持的。

And int[][] is not an array of primitive types, int[] is a complex type in this context. Based on discussions here and here it seems to be a conscious decision.

int[][] 不是原始类型的数组,int[] 在这个上下文中是一个复杂类型。根据这里这里的讨论,这似乎是一个有意的决定。

As for workarounds since .NET 7 you can use AsParameters attribute and do something like the following:

至于解决方法,自 .NET 7 开始,您可以使用 AsParameters 属性并执行类似以下操作:

app.MapGet("/testarr", ([AsParameters] Dto dto)
	=> ... );

public class MyJaggedIntArrWrapper
{
	public int[][] Arr { get; set; }
	public static ValueTask<MyJaggedIntArrWrapper> BindAsync(HttpContext context)
	{
		return ValueTask.FromResult(new MyJaggedIntArrWrapper
		{
			Arr = new int[0][] // actual parsing from context ...
		});
	}
}

public class Dto
{
	public string? A { get; set; }
	public MyJaggedIntArrWrapper SomeNameDoesNotMatter { get; set; }
}

Which will successfully bind a for /testarr?a=5&vals[0][0]=5&vals[0][1]=6&vals[1][0]=6&vals[1] query and will invoke MyJaggedIntArrWrapper.BindAsync thus removing the need to manually bind what can be bound automatically.

这将成功地将 /testarr?a=5&vals[0][0]=5&vals[0][1]=6&vals[1][0]=6&vals[1] 查询绑定到 a,并将调用 MyJaggedIntArrWrapper.BindAsync,从而消除了手动绑定的需要。

英文:

First of all int[][] is not multidimensional array (that would be int[,]) but a jagged one.

I would argue that this is pretty much covered in the docs:

> Binding query strings or header values to an array of complex types is supported when the type has TryParse implemented.

And int[][] is not an array of primitive types, int[] is a complex type in this context. Based on discussions here and here it seems to be a conscious descision.

As for workarounds since .NET 7 you can use AsParameters attribute and do something like the following:

app.MapGet("/testarr", ([AsParameters] Dto dto)
	=> ... );

public class MyJaggedIntArrWrapper
{
	public int[][] Arr { get; set; }
	public static ValueTask<MyJaggedIntArrWrapper> BindAsync(HttpContext context)
	{
		return ValueTask.FromResult(new MyJaggedIntArrWrapper
		{
			Arr = new int[0][] // actual parsing from context ...
		});
	}
}

public class Dto
{
	public string? A { get; set; }
	public MyJaggedIntArrWrapper SomeNameDoesNotMatter { get; set; }
}

Which will successfully bind a for /testarr?a=5&vals[0][0]=5&vals[0][1]=6&vals[1][0]=6&vals[1] query and will invoke MyJaggedIntArrWrapper.BindAsync thus removing the need to manually bind what can be bound automatically.

huangapple
  • 本文由 发表于 2023年5月11日 05:30:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76222669.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定