skip to Main Content

I have a method on a dotnet controller like this

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

Using a GetCarFilter class that looks like this

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; }
}

Using a querystring like this

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

Dotnet will automagically pull out values from the querystring and convert them to ints and bools for some properties by not the ObjectId (which is from the MongoDB C# driver)

I have tried creating a custom type converter and adding it to the property like so

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

But it never calls my code. Somehow dotnet can take the querystring values for NumberOfWheels and HasHeadLights and can convert them to an int and bool but will not do the same for my ModelId. Is there a way I can tell dotnet this is an ObjectId and this is how you convert it from a string?

2

Answers


  1. Chosen as BEST ANSWER

    Found that asp.net core is using model binders and you can build your own.

    Just create a class the implements 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;
        }
    }
    

    Then register it with MVC/WebAPI

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

    Be sure to insert instead of add because by default asp.net will place a catchall binder at the end of the list. If you add yours will be placed behind this catchall and never be called.

    After doing this any controller parameter marked with a FromBody, FromQueryString, FromRoute, FromHeader, FromForm will be automatically converted. It even lets you provide validation errors in case your conversion fails!


  2. try this

       .....
      public string modelId { get;  set; }
      public ObjectId ModelId  { get { return new ObjectId(modelId ); } }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search