Hoborg Hoborg - 12 days ago 11
C# Question

ASP.NET MVC: types cast in model binding

Is there a way to cast parameter in the controller's action from one type to another in ASP.NET MVC 5?

Example. Action method is the following:

public string Test(Guid input) {
return "";
}


If the method is invoked with parameters "input=hello" then I get the error with the message: "The parameters dictionary contains a null entry for parameter 'input' of non-nullable type 'System.Guid' for method 'System.String Test(System.Guid)' in 'RealtyGuide.WebSite.Controllers.HomeController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."

What I want is try to cast the parameter to Guid according to specific (custom) rules. Is it a question on model binding? What are the possible ways to solve this task? Sorry for my English.

Answer

According to the comments from @AndreiV and @AntP the decisions are: 1) if the string is a correct Guid-string then it is binded automatically (nothing else is needed), 2) if the string is not a correct Guid-string one should 2.1) make conversion in the action's body (to my mind it entails code duplication), or 2.2) set up the custom (user-defined) model binder. The following is the code for the last approach (model binder).

// This is an example. 
// Note the returning of null which is undesired.
// Also it does not deal with exceptions handling. Use it only as a stub.
    public class ExtendedModelBinder : DefaultModelBinder {
            public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
                if (!(bindingContext.ModelType == typeof(Guid)))
                    return base.BindModel(controllerContext, bindingContext);
                if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName))
                    return null;
                string input = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
                if (string.IsNullOrEmpty(input))
                    return null;
                Guid g;
                if (Guid.TryParse(input, out g))
                    return g;
                var bytes = HttpServerUtility.UrlTokenDecode(s);
                var result = new Guid(bytes);
                return result;
            }
        }

Registration in Application_Start is necessary:

ModelBinders.Binders.Add(typeof(Guid), new RealtyGuide.WebSite.Extensions.MyModelBinder());

One may use attributes instead of registration of the binder globally (see Method parameter with (?attribute?) in barkets ), but I'll not use it, because it entails unnecessary code duplication in my task.

Refs: http://www.codeproject.com/Articles/605595/ASP-NET-MVC-Custom-Model-Binder http://msdn.microsoft.com/en-us/magazine/hh781022.aspx
HttpServerUtility.UrlTokenDecode only seems to return null