lundi 23 novembre 2020

Generic method for converting QueryString to POCO class

I am trying to write a generic class to cast query string passed into a blazor page into a plain c# class. I want to reuse this, rather than specifying specific querystring parameters on every page declaration.

So far I am using reflection and generics but I'm getting an error when trying to pass the property type to the underlying extension methods;

Generic conversion class;

        public static TSearchParams GetSearchParamsFromQueryString<TSearchParams>(this Uri uri) where TSearchParams : SearchParams, new()
        {
            var searchParams = new TSearchParams();

            foreach (PropertyInfo prop in searchParams.GetType().GetProperties())
            {
                var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
                if (type == typeof(Enum) || type.IsEnum)
                {
                    prop.SetValue(searchParams, uri.GetEnumFromQueryString<prop.PropertyType> (prop.Name));
                } 
                else
                {
                    prop.SetValue(searchParams, uri.GetValueFromQueryString<prop.PropertyType>(prop.Name);
                }
            }

This is failing when I try to use prop.PropertyType in uri.GetEnumFromQueryString<prop.PropertyType> (prop.Name)); for both get values and get enum.

Other relevent code;

POCO class

public class MySearchParameters: SearchParams 
{
        public MyEnumType? MyEnum { get; set; }
        public DateTime? DateStart { get; set; }
        public long? BranchId { get; set; }
        ///.. other types removed for brevity
}

To extract a specific value from a URI (this works in isolation);

        public static T GetValueFromQueryString<T>(this Uri uri, string key)
        {
            if (QueryHelpers.ParseQuery(uri.Query).TryGetValue(key, out var valueFromQueryString))
            {
                if (typeof(T) == typeof(int?) && int.TryParse(valueFromQueryString, out var valueAsInt))
                {
                    return (T)(object)valueAsInt;
                }

                if (typeof(T) == typeof(long?) && long.TryParse(valueFromQueryString, out var valueAsLong))
                {
                    return (T)(object)valueAsLong;
                }
                ///.. other types removed for brevity

                // otherwise get a string
                return (T)(object)valueFromQueryString.ToString();
            }

            return default(T);
        }

To extract an enum from a URI (this works in isolation);

        public static Nullable<TEnum> GetEnumFromQueryString<TEnum>(this Uri uri, string key) where TEnum : struct
        {
            if (QueryHelpers.ParseQuery(uri.Query).TryGetValue(key, out var valueFromQueryString))
            {
                TEnum res = (TEnum)Enum.Parse(typeof(TEnum), valueFromQueryString);
                if (!Enum.IsDefined(typeof(TEnum), res)) return null;
                return res;
            }
            return null;
        }

What is the correct way to cast the types during relfection? Or, is there a simpler way to convert all QueryString parameters to a C# class?





Aucun commentaire:

Enregistrer un commentaire