繁体   English   中英

为模型绑定到集合类型指定相等比较器

[英]Specify equality comparer for model binding to collection types

我有一个API,您可以在其中指定要获取的名称列表。 不允许使用重复的名称,如果两个名称之间只有大小写不同,则将它们视为重复的名称。

GET /api/people?names=john&names=alice

在我自己的.NET代码中,我将使用自定义的相等比较器将所有这些名称收集在HashSet

var names = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

但是我认为ASP.NET Core模型绑定没有足够的灵活性。

// GET /api/people?names=john&names=JOHN
[HttpGet("api/people")]
public GetPeople([FromQuery] HashSet<string> names)
{
    // this works but names contains both john and JOHN
}

我必须更改什么才能使names集仅包含john而不包含JOHN?

您可以在方法标题中使用List<string> ,然后根据您的规则将其加载到HashSet中:

public GetPeople([FromQuery] List<string> names)
{
    var hashed = new HashSet<string>(names, StringComparer.OrdinalIgnoreCase);
}

作为替代方案,您可以研究自定义模型绑定,但是只有在多个地方都需要时,才值得付出努力。

您可以使用设置为始终使用StringComparer.OrdinalIgnoreCaseHashSet<string>派生类:

// Case Insensitive String HashSet, or CIStringSet for short.
public class CIStringSet : HashSet<string>
{
    public CIStringSet() : base(StringComparer.OrdinalIgnoreCase)
    {
    }
}

这样,使用它非常简单:

public object GetPeople([FromQuery] CIStringSet names)
{
    return Json(names);
}

经过测试,可以正常工作:

在此处输入图片说明

一种可能的解决方案是创建一个指定相等比较器的派生类型,然后绑定到该派生类型。

public class StringSetIgnoreCase : HashSet<string>
{
    public StringSetIgnoreCase()
        : base(StringComparer.OrdinalIgnoreCase)
    {
    }
}

// GET /api/people?names=john&names=JOHN&names=alice
[HttpGet("api/people")]
public GetPeople([FromQuery] StringSetIgnoreCase names)
{
    // names contains only john and alice
}

该解决方案的问题在于扩展性不佳:您必须为每种比较类型创建一个派生类型。

一个完全静态的解决方案,完全没有错误处理,但是可以解决以下问题

活页夹:

public Task BindModelAsync(ModelBindingContext bindingContext)
{
    var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

    bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

    var values = valueProviderResult.Values.Select(value => value).ToHashSet(StringComparer.OrdinalIgnoreCase);

    bindingContext.Result = ModelBindingResult.Success(values);

    return Task.CompletedTask;
}

终点:

[HttpGet]
public IActionResult Test([ModelBinder(typeof(HashSetBinder))] IEnumerable<string> values)
{
    return Ok(new
    {
        values
    });
}

然后只需打开网址,例如这样:

http:// localhost:5000 / api / mycontroller?values = 1&values = 1&values = 1&values = 2

结果将是{"values":["1","2"]}

可以使用modelbinding-provider在全球范围内进行注册,但为此需要执行更多检查。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM