I was trying to use AutoQuery with a NodaTime.LocalDate
on a query parameter and I get the following exception when I try to filter using that date field, specifically >MyDate=2020-01-01
(ordering is unaffected):
[MyEndpoint: 5/23/2016 4:19:51 PM]: [REQUEST: {}] System.InvalidCastException: Invalid cast from 'System.String' to 'NodaTime.LocalDate'. at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider) at ServiceStack.TypedQuery`2.AppendUntypedQueries(SqlExpression`1 q, Dictionary`2 dynamicParams, String defaultTerm, IAutoQueryOptions options, Dictionary`2 aliases) at ServiceStack.TypedQuery`2.CreateQuery(IDbConnection db, IQueryDb dto, Dictionary`2 dynamicParams, IAutoQueryOptions options) at ServiceStack.AutoQuery.CreateQuery[From](IQueryDb`1 dto, Dictionary`2 dynamicParams, IRequest req) at ServiceStack.AutoQueryServiceBase.Exec[From](IQueryDb`1 dto) at ServiceStack.Host.ServiceRunner`1.Execute(IRequest request, Object instance, TRequest requestDto)
I tracked it down to this line of code that uses Convert.ChangeType(...)
because NodaTime.LocalDate
is a struct
and not an enum
:
var value = strValue == null ?
null
: isMultiple ?
TypeSerializer.DeserializeFromString(strValue, Array.CreateInstance(fieldType, 0).GetType())
: fieldType == typeof(string) ?
strValue
: fieldType.IsValueType && !fieldType.IsEnum ? //This is true for NodaTime.LocalDate
Convert.ChangeType(strValue, fieldType) : //NodaTime.LocalDate does not implement IConvertible, so this throws
TypeSerializer.DeserializeFromString(strValue, fieldType);
I'm using my NodaTime ServiceStack serialization library so the behavior of TypeSerializer.DeserializeFromString(strValue, fieldType)
is what I actually want in this case.
Workarounds I see are:
MyDateDateBetween=2020-01-01,9999-12-31
in the query string as that code path uses the custom serialization I have specified (cumbersome)DateTime
instead of NodaTime.LocalDate
(I want to use NodaTime.LocalDate
)NodaTime.LocalDate
implements IConvertible
(not likely)Is there another way to get auto query filters to work with value types that don't implement IConvertible
?
I've just added wrapped these lines in a new ChangeTo()
extension method with an extra check to check for implementing IConvertible
in this commit:
public static object ChangeTo(this string strValue, Type type)
{
if (type.IsValueType && !type.IsEnum
&& type.HasInterface(typeof(IConvertible)))
{
try
{
return Convert.ChangeType(strValue, type);
}
catch (Exception ex)
{
Tracer.Instance.WriteError(ex);
}
}
return TypeSerializer.DeserializeFromString(strValue, type);
}
And changed AutoQuery to use it so NodaTime's LocalDate should now fall through to the TypeSerializer.
This change is available from v4.0.57 that's now available on MyGet.