Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding a string to a parameter using an implicit operator in ASP MVC

Tags:

c#

asp.net

I've looked around on SO, I can't find a sufficient answer to my question.

I have a wrapper class called Title defined like this

public class Title
{
    private readonly string _title;

    public Title (string title) {
        _title = title;
    }

    public static implicit operator Title(string title)
    {
        return new Title(title);
    }
}

I am using this class in an ASP MVC project. Right now I defined a controller like this:

public ActionResult Add(string title)
{
    //stuff
}

and this works fine. However, I wish to automatically bind the posted string value to the Title constructor, thus accepting a Title instead of a string as a parameter:

public ActionResult Add(Title title)
{
    //stuff
}

This however, does not work, as I will get the error: The parameters dictionary contains a null entry for parameter, meaning the model binder can't bind the string to the Title parameter.

The HTML responsible for posting the title data:

<form method="post" action="/Page/Add" id="add-page-form">                
    <div class="form-group">
        <label for="page-title">Page title</label>
        <input type="text" name="title" id="page-title">
    </div>
</form>

My question exists of two parts:

1. Why is it not possible to do this, I would expect the bodel binder to use the defined implicit operator to create a Title instance.

2. Is there a way to still accomplish getting the desired behavior, without explicitly creating a modelbinder?

like image 379
Glubus Avatar asked Apr 30 '16 15:04

Glubus


2 Answers

Though I am late, just for the sake of covering all available options: you could implement your own TypeConverter, as follows:

public class TitleConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) ? true : base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value is string)
            return new Title((string)value);

        return base.ConvertFrom(context, culture, value);
    }
}

[TypeConverter(typeof(TitleConverter))]
public class Title
{
    ...
}

This approach is especially useful if you need to instantiate your class from different types

like image 113
Herman Kan Avatar answered Sep 30 '22 04:09

Herman Kan


As per you questions:

  1. The model binder is going to call new Title(). Which he can't. Then he would try to set a Title property. Which he can't find. No, the default binder does not call implicit conversions. The algorythm he uses is different.
  2. No, you don't need a custom binder, if you accept to change your Model, which is completely wrong in according to the behaviour of the default model binder.

The implicit conversion doesn't matter at all for Action binding.

The default model binder takes a big dictionary of values, gathered from the various parts of the request, and tries to insert them into properties.

So, if you want to use your Title as an Action parameter, your best bet is to make the Title class Binder-friendly, so to speak:

/* We call the class TitleModel in order not to clash
 * with the Title property.
 */
public class TitleModel
{
    public string Title { get; set; }

    /* If you really need the conversion for something else...
    public static implicit operator Title(string title)
    {
        return new Title { Title = title };
    }
    */
}

Everything should work as it is on the client side.

If you cannot (or don't want to) change your model class, then you can go for a custom model binder. But I don't think you really need it.

like image 32
A. Chiesa Avatar answered Sep 30 '22 04:09

A. Chiesa