ASP.NET核心ModelBindingContext仅使用1个值提供程序(忽略RoutedDataValueProvider)

ASP.NET核心ModelBindingContext仅使用1个值提供程序(忽略RoutedDataValueProvider),asp.net,asp.net-mvc,asp.net-core,model-binding,custom-model-binder,Asp.net,Asp.net Mvc,Asp.net Core,Model Binding,Custom Model Binder,我目前正在将一个ASP.NET库转换为ASP.NET Core,它需要大量的模型绑定,我无法解决的一个问题是,如果模型绑定器希望尝试通过值提供程序从(从)查询和(从)路由(如中,它们有不同的绑定源)获取值,那么它只能访问一个,导致绑定失败,因为它找不到目标值 在调试应用程序时,我可以看到ModelBindingContext在其私有的OriginalValueProvider中确实有两个值提供程序,但当尝试访问ValueProvider时,它只返回其中一个提供程序(在本例中,仅返回QuerySt

我目前正在将一个ASP.NET库转换为ASP.NET Core,它需要大量的模型绑定,我无法解决的一个问题是,如果模型绑定器希望尝试通过值提供程序从(从)查询和(从)路由(如中,它们有不同的绑定源)获取值,那么它只能访问一个,导致绑定失败,因为它找不到目标值

在调试应用程序时,我可以看到ModelBindingContext在其私有的OriginalValueProvider中确实有两个值提供程序,但当尝试访问ValueProvider时,它只返回其中一个提供程序(在本例中,仅返回QueryStringValueProvider,不返回RouteValueProvider)

这似乎不是常规ASP.NET中的行为,因为现有的应用程序很高兴地使用两个值提供程序来查找其值,并且这样做很成功。在System.Web.Http.ModelBinding->Microsoft.AspNetCore.Mvc.ModelBinding之间是否存在一些突破性的变化?是否有一些必须启用的选项?请提供一些指导

下面是我调试中的一些屏幕上限:

System.Web.Http原始文件:

Microsoft.AspNetCore.Mvc返工:

Microsoft.AspNetCore.Mvc原始价值提供者证明:

编辑: 根据要求,下面是一些代码片段:

控制器:

[HttpGet]
[Route("accounts/{accountId}/catalogs/{catalogId}/medias", Name = "GetCatalogMedias")]
[ProducesResponseType(typeof(IList<MediaResponse>), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetCatalogMedias(
    [FromRoute] string accountId,
    [FromRoute] string catalogId,
    [FromQuery, SortingFieldDefaultValue("created", SortingDirection.Desc)] IEnumerable<SortingField> sort,
    [FromQuery] QueryTree<MediaResponse> q = null,
    [FromQuery, Range(0, int.MaxValue)] int offset = 0,
    [FromQuery, Range(1, 200)] int limit = 200)
{
    if (!ModelState.IsValid)
    {
        return await _responseFactory.CreateBadRequest(ModelState);
    }
[HttpGet]
[路由(“accounts/{accountId}/catalogs/{catalogId}/medias”,Name=“GetCatalogMedias”)]
[产品响应类型(typeof(IList),(int)HttpStatusCode.OK)]
公共异步任务GetCatalogMedias(
[FromRoute]字符串accountId,
[FromRoute]字符串catalogId,
[FromQuery,SortingFieldDefaultValue(“created”,SortingDirection.Desc)]IEnumerable sort,
[FromQuery]查询树q=null,
[FromQuery,Range(0,int.MaxValue)]int offset=0,
[FromQuery,Range(1200)]int limit=200)
{
如果(!ModelState.IsValid)
{
返回wait _responseFactory.CreateBadRequest(ModelState);
}
活页夹:

public class CatalogMediasQueryModelBinder<T> : BaseBinder, IModelBinder
{
    private readonly QueryModelBinder _queryModelBinder;

    public CatalogMediasQueryModelBinder(QueryModelBinder queryModelBinder)
    {
        _queryModelBinder = queryModelBinder;
    }

    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var success = TryGetValueProviderResult(bindingContext, out var catalogId, "catalogId");

        if (!success)
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Missing catalogId");
            return;
        }
        //...
公共类目录MediaSqueryModelBinder:BaseBinder、IModelBinder
{
专用只读QueryModelBinder\u QueryModelBinder;
公共目录MediaSqueryModelBinder(QueryModelBinder QueryModelBinder)
{
_queryModelBinder=queryModelBinder;
}
公共异步任务BindModelAsync(ModelBindingContext bindingContext)
{
var success=TryGetValueProviderResult(bindingContext,out-var-catalogId,“catalogId”);
如果(!成功)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName,“缺少catalogId”);
返回;
}
//...
基层粘合剂:

public class BaseBinder
{
    protected bool TryGetValueProviderResult(ModelBindingContext bindingContext, out string result, string findValue = null)
    {
        if (bindingContext?.ValueProvider == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }
        result = null;
        var modelName = findValue ?? bindingContext.ModelName;
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
        if (valueProviderResult == ValueProviderResult.None)
        {
            return false;
        }

        bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);

        var value = valueProviderResult.FirstValue;

        if (string.IsNullOrWhiteSpace(value))
        {
            return false;
        }

        result = value;
        return true;
    }

    protected Task SetModelBindingResult<T>(ModelBindingContext bindingContext, T value)
    {
        bindingContext.Result = ModelBindingResult.Success(value);
        return Task.CompletedTask;
    }
}
公共类BaseBinder
{
受保护的bool TryGetValueProviderResult(ModelBindingContext,输出字符串结果,字符串findValue=null)
{
if(bindingContext?.ValueProvider==null)
{
抛出新ArgumentNullException(nameof(bindingContext));
}
结果=空;
var modelName=findValue??bindingContext.modelName;
var valueProviderResult=bindingContext.ValueProvider.GetValue(modelName);
如果(valueProviderResult==valueProviderResult.None)
{
返回false;
}
bindingContext.ModelState.SetModelValue(modelName、valueProviderResult);
var值=valueProviderResult.FirstValue;
if(string.IsNullOrWhiteSpace(value))
{
返回false;
}
结果=值;
返回true;
}
受保护的任务SetModelBindingResult(ModelBindingContext,T值)
{
bindingContext.Result=ModelBindingResult.Success(值);
返回Task.CompletedTask;
}
}

ModelBindingContext在其私有的OriginalValueProvider中确实有两个值提供程序,但是当尝试访问ValueProvider时,它只返回其中一个提供程序-在这种情况下,只有QueryStringValueProvider而不是RouteValueProvider)
请共享自定义模型绑定器的代码和相应的控件您希望同时从route和querystring传递数据的oller操作。@Feihan,我已编辑了文章并包含了必要的代码段,以关联我的语句和我的屏幕标题。
只有querystring ValueProvider而不是RouteValue Provider
不能再现相同的问题,就像您在代码中所做的那样,指定了“[FromRoute]字符串catalogId'作为参数,这将是。感谢您尝试@FeiHan;您知道这可能是什么原因,或者是否有一些可能的解决方案吗?我一直在考虑尝试制作一个自定义的ValueProvider,看看是否可以修复它,但既然这“应该”起作用,那么我也不太愿意向它扔更多的数字管道胶带。