Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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.

share|improve this question
add comment

1 Answer

up vote 3 down vote accepted

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());
share|improve this answer
1  
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. –  jessegavin Nov 8 '13 at 20:21
 
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) –  Yevgeniy.Chernobrivets Nov 8 '13 at 21:27
 
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. –  jessegavin Nov 8 '13 at 21:32
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.