如何为自定义类型创建 asp.net 控制器自动绑定属性?

How can I make an asp.net controller auto bind properties for a custom type?

我在 dotnet 控制器上有一个这样的方法

 [HttpGet()]
 public async Task<IActionResult> GetCar(
        [FromQuery] GetCarFilter filters
 )

使用如下所示的 GetCarFilter class

public class GetCarFilter
{
    public GetCarFilter()
    {
    }

    public string? SearchTerm { get; set; }

    public int NumberOfWheels { get; set; }

    public bool HasHeadLights { get; set; }

    public ObjectId ModelId { get; set; }
}

使用这样的查询字符串

http://somedomain.com/cars?NumberOfWheels=4&HasHeadlights=true?ModelId=623c79ac554f9d15425f93c2

Dotnet 将自动从查询字符串中提取值,并通过非 ObjectId(来自 MongoDB C# 驱动程序)将它们转换为某些属性的整数和布尔值

我试过创建自定义类型转换器并将其添加到 属性 中,就像这样

[TypeConverter(typeof(MyObjectIdConverter))]
public ObjectId ModelId { get; set; }

但它从不调用我的代码。 dotnet 可以通过某种方式获取 NumberOfWheels 和 HasHeadLights 的查询字符串值,并将它们转换为 int 和 bool,但不会对我的 ModelId 执行相同的操作。有什么方法可以告诉 dotnet 这是一个 ObjectId 并且这是从字符串转换它的方式吗?

试试这个

   .....
  public string modelId { get;  set; }
  public ObjectId ModelId  { get { return new ObjectId(modelId ); } }

发现 asp.net 核心正在使用模型绑定器,您可以 build your own

只需创建一个 class 工具 IModelBinder

public class ObjectIdModelBinder : IModelBinder
{
    public ObjectIdModelBinder()
    {
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        var modelName = bindingContext.ModelName;

        // Try to fetch the value of the argument by name
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);

        if (valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);

        var value = valueProviderResult.FirstValue;

        // Check if the argument value is null or empty
        if (string.IsNullOrEmpty(value))
        {
            return Task.CompletedTask;
        }

        if (!ObjectId.TryParse(value, out var id))
        {
            // Not a valid object id
            bindingContext.ModelState.TryAddModelError(
                modelName, $"{modelName} must be a valid Mongo Object Id string.");

            return Task.CompletedTask;
        }

        bindingContext.Result = ModelBindingResult.Success(id);
        return Task.CompletedTask;
    }
}

然后用MVC/WebAPI

注册
services.AddControllers(config =>
{
                config.ModelBinderProviders.Insert(0, new ObjectIdModelBinderProvider());
            
});

一定要插入而不是添加,因为默认情况下 asp.net 会在列表末尾放置一个 catchall 活页夹。 如果您添加,您的活页夹将放在后面这个包罗万象,永远不会被调用。

执行此操作后,任何标有 FromBody、FromQueryString、FromRoute、FromHeader、FromForm 的控制器参数都将自动转换。它甚至可以让您在转换失败时提供验证错误!