英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论