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

时间:2018-07-31 09:18:37

标签: c# asp.net-core model-binding

我有一个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,我需要更改什么?

4 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:2)

您可以使用设置为始终使用HashSet<string>的{​​{1}}派生类:

StringComparer.OrdinalIgnoreCase

使用它非常简单,就像这样:

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

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

  

enter image description here

答案 2 :(得分:0)

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

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
}

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

答案 3 :(得分:0)

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

活页夹:

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在全球范围内注册,但是为此,它需要执行更多检查。