2

Our system has a set of known "Contract Types" which have a code and a name.

public struct ContractType {
  public string Code { get; set; }
  public string Name { get; set; }
}

I have an MVC controller with a method like this.

[HttpGet]
public ActionResult Search(SearchOptions options) {
   // returns search results
}

SearchOptions contains many parameters (including an array of ContractType)

public class SearchOptions {
  public ContractTypes[] Contracts { get; set; }
  // other properties
}

I would like asp.net MVC to automatically translate the contract type codes into an array of contract types on the SearchOptions model. For example, I want the MVC model binder to take a a querystring like this...

http://abc.com/search?contracts=ABC&contracts=XYZ&foo=bar

and populate SearchOptions so that it looks like the following data structure

{
  Contracts : [
    { Code : "ABC", Name: "ABC Contract Name" },
    { Code : "XYZ", Name: "XYZ Contract Name" }
  ],
  // other properties
}

I have a method available which will accept a contract type code and return the appropriate ContractType.

public ContractType GetContractTypeByCode(string code) {
 // code which returns a ContractType
}

I am not clear on whether I need to be using a custom model binder or a value provider. Any help is appreciated.

1 Answer 1

3

I think you should use ModelBinder. Something like this

public class SearchOptionsDataBinder : DefaultModelBinder
{
  public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
  {
    if (bindingContext.ModelType == typeof(SearchOptions))
    {
      var baseResult = (SearchOptions)base.BindModel(controllerContext, bindingContext);
      var request = controllerContext.HttpContext.Request;

      baseResult.Contracts = request.QueryString
                                    .GetValues("contracts")
                                    .Select(GetContractTypeByCode)
                                    .Where(c => !string.IsNullOrEmpty(c.Code))
                                    .ToArray();
      return baseResult;
    }

    return base.BindModel(controllerContext, bindingContext);        
  }
} 

And then add custom model binder to Application_Start:

ModelBinders.Binders.Add(typeof(SearchOptions), new SearchOptionsDataBinder());
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you so much. There were a few problems with the code that I was able to iron out, but your basic solution worked like a charm. I edited your answer with the code I ended up using.
Glad to help. Also maybe it is better to put check for empty value before select so you don't pass any empty values to GetContractTypeByCode method. .Where(c => !string.IsNullOrEmpty(c)).Select(GetContractTypeByCode)
My implementation of GetContractTypeByCode will currently return a ContractType instance with null value for the Code if it isn't a valid contract type. Kind of weird I know. I should clean that up.
Can be the string property "contracts" replaced by some code which means the name of property in the model?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.