Typeconverter does not work in asp.net core
Asked Answered
E

1

13

I have Amount stored in the database as decimal. I want to show that value on UI with thousand separator. I can add [DisplayFormat(DataFormatString = "{0:N2}", ApplyFormatInEditMode = true)] attribute on amount property and that would display number with thousand separator however when i POST the value back to server, the MVC model binding would not work because of commas.

I have created a custom type converter that converts from decimal to string and then string to decimal

public class NumberConverter : TypeConverter
{
    public override bool CanConvertFrom(
        ITypeDescriptorContext context,
        Type sourceType)
    {
        if (sourceType == typeof(decimal))
        {
            return true;
        }
        return base.CanConvertFrom(context, sourceType);
    }
    public override object ConvertFrom(ITypeDescriptorContext context,
        CultureInfo culture, object value)
    {
        if (value is decimal)
        {
            return string.Format("{0:N2}", value);
        }
        return base.ConvertFrom(context, culture, value);
    }
    public override bool CanConvertTo(ITypeDescriptorContext context,
        Type destinationType)
    {
        if (destinationType == typeof(decimal))
        {
            return true;
        }
        return base.CanConvertTo(context, destinationType);
    }
    public override object ConvertTo(ITypeDescriptorContext context,
        CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(decimal) && value is string)
        {
            return Scrub(value.ToString());
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }

    private decimal Scrub(string modelValue)
    {
        NumberStyles _currencyStyle = NumberStyles.Currency;
        CultureInfo _culture = new CultureInfo("en-US");

        var modelDecimal = 0M;
        decimal.TryParse(
           modelValue,
           _currencyStyle,
           _culture,
           out modelDecimal
       );

        return modelDecimal;
    }
}

and then i applied it on one of the model property. Note that model may have other decimal properties which may not required this conversion.

public class MyModel
{

    [TypeConverter(typeof(NumberConverter))]
    [Display(Name = "Enter Amount")]
    public decimal Amount { get; set;}

    public string Name { get; set; }
}

Index.cshtml

<form asp-action="submit" asp-controller="home">
    @Html.EditorFor(m => m.Amount)
    <button type="submit" class="btn btn-primary">Save</button>
</form>

However the converter code never gets fired. When i put break point in NumberConverter none of the break point hit. Do i need to register type converter anywhere? I am using asp.net core.

Ephesian answered 12/4, 2017 at 20:5 Comment(1)
I noticed that TypeConverter works with Json.net serialization but not with System.Text.Json, which is default in ASP.NET Core. We use a custom Date struct as a field in data contract and expect to receive it in the body of the POST method. Default .NET Core serializer failed to bind the contents of the request, so the data contract class was null. Switching to Json.net solved the problem.Diphyllous
F
9

Based on my observations asp.net core doesn't take into account properties decorated by TypeConverters.

So it only supports TypeConverters that are decorating actual type class declaration.

Works

[TypeConverter(typeof(MyModelStringTypeConverter))]
public class MyModel
{

}

Doesn't work

public class OtherModel
{
    [TypeConverter(typeof(MyModelStringTypeConverter))]
    public MyModel MyModel { get;set; }
}
Finis answered 13/6, 2018 at 14:4 Comment(3)
I hit the same issue - inside model I have collection/set of values which I want to parse from string - so far I came up with writing simple converter and "registering" it from Startup this way: TypeDescriptor.AddAttributes( typeof(IReadOnlyCollection<string>),new TypeConverterAttribute( typeof(FooTypeConverter))); ... but I don't quite like this approach :(Drier
@Zdeněk do you know if there is a way to register (at Startup) a class TypeConverter instead of having to decorate that class with the TypeConverter attribute?Pitarys
@Pitarys Hi, unfortunately can't help here. I haven't used TypeDescriptor for years now. These days, I would look towards ActionFilterAttribute or ValidationAttribute based on scenario, or simply treat DTOs as stupid objects and then do proper mapping into domain object later "manually".Drier

© 2022 - 2024 — McMap. All rights reserved.