KingKerosin
KingKerosin

Reputation: 3841

Typeconverter not working for single properties

I have a simple TypeConverter to convert a comma-separated string into an IEnumerable<T> to shorten the url when calling my API-endpoints.

Therefore I have an request-object which is set on client and passed to the server. So, on server it's the same object.

This is what the type-converter looks like:

public class EnumerableTypeConverter : TypeConverter
{
    private readonly Type _innerType;
    private readonly MethodInfo _enumerableCastMethodInfo = typeof(Enumerable).GetMethod(nameof(Enumerable.Cast));

    public IEnumerableTypeConverter(Type type)
    {
        // check if the type is somewhat ienumerable-like
        if (type.BaseType != null && type.BaseType.IsGenericType && typeof(IEnumerable<>).MakeGenericType(type.BaseType.GetGenericArguments()[0]).IsAssignableFrom(type.BaseType) && type.BaseType.GetGenericArguments().Length == 1)
        {
            _innerType = type.BaseType.GetGenericArguments()[0];
        }
        else
        {
            throw new ArgumentException("Incompatible type", nameof(type));
        }
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) => (destinationType.BaseType != null && typeof(IEnumerable<>).MakeGenericType(destinationType.BaseType.GetGenericArguments()[0]).IsAssignableFrom(destinationType.BaseType)) || base.CanConvertFrom(context, destinationType);

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        var source = value as string;
        if (source == null)
            return base.ConvertFrom(context, culture, value);

        var temp = source.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => TypeDescriptor.GetConverter(_innerType).ConvertFromInvariantString(s));
        var castMethod = _enumerableCastEmthMethodInfo.MakeGenericMethod(_innerType);
        var casted = castMethod.Invoke(null, new object[] {temp});
        return casted;
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        var s = value as IEnumerable<string>;
        return s != null ? string.Join(",", s) : base.ConvertFrom(context, culture, value);
    }
}

A sample request could look something like

public class MyRequest 
{
    [TypeConverter(typeof(EnumerableTypeConverter))]
    public IEnumerable<string> Names {get;set;}

    [TypeConverter(typeof(EnumerableTypeConverter))]
    public IEnumerable<Guid> Ids {get;set;}
}

I've decorated the properties with [TypeConverter(typeof(EnumerableTypeConverter))]-attribute. However, the ConvertFrom-method is never called. When I add the attribute on a class instead of a property it is working.

Upvotes: 4

Views: 2393

Answers (3)

sich
sich

Reputation: 630

You can't attach a TypeConverter to a view model property in ASP.NET Core as of now. Here is the issue tracking this problem https://github.com/dotnet/aspnetcore/issues/8857. I believe only XAML frameworks support type converters on properties.

Also note that since ASP.NET Core moved JSON serialization from Newtonsoft.JSON to System.Text.Json TypeConverter is not supported on json payloads (e.g. POST requests) even if it's attached to a class. It only works when a value to be bound is provided as a simple string by the model binding. You can use custom JsonConverter to work around this.

I know this is late answer, but the problem still exists after 5 years.

Upvotes: 0

salex
salex

Reputation: 11

For those who are still interested in possible workaround look at this thread. Issue could be because of converter being defined in an assembly different than your app domain.

Upvotes: 1

Stephane Delcroix
Stephane Delcroix

Reputation: 16232

the TypeConverterAttribute (used in [TypeConverter]) is different from the base class TypeConverter.

For the EnumerableTypeConverter to be invoked, it needs to be instantiated, and it can only be automagically instantiated if a parameterless constructor exists.

In your case, you might want to have 2 converters, a EnumerableStringConverter and a EnumerableGuidConverter, or use a generic one:

public class EnumerableTypeConverter<T> : TypeConverter
{
}

and you can decorate your properties like this:

public class MyRequest 
{
    [TypeConverter(typeof(EnumerableTypeConverter<string>))]
    public IEnumerable<string> Names {get;set;}

    [TypeConverter(typeof(EnumerableTypeConverter<Guid>))]
    public IEnumerable<Guid> Ids {get;set;}
}

Upvotes: 0

Related Questions