So we noticed a breaking change in the ConvertTo logic when we attempted to upgrade to 5.5.
The issue is demonstrated in here https://gistlyn.com/?gist=d6bf646ece994da634001303fae3c738
It appears to be from change in ConvertTo in commit 3c0c728be42f872fb0550e91d9e4784ac4393814
We only saw it in the case were our web developer had inherited the DTO type in his viewmodels, and then was using ConvertTo to go back to the DTO for POSTing. The new behavior simply returns the original object back if you are trying to convert to the parent type.
As we saw no release notes on this behavior change assuming it wasn’t intended.
The ConvertTo<T> API’s purpose is to convert it into the target type, if it can be cast into the target type it will just return it as it’s more efficient.
If you wanted to clone the Type you can use:
var clone = TypeSerializer.Clone(obj);
Which is a deep copy that essentially serializes/de-serializes it. Alternatively you can use:
var to = from.CreateCopy<To>();
Which for reference type does:
var to = typeof(To).CreateInstance();
to.PopulateWith(from);
Which was the behavior you had previously, essentially a shallow copy.
FYI: Ran into this same issue. It was a little hard to track down that this ConvertTo change was the issue. No problem keeping the new v5.5 ConvertTo way. Thanks for posting the clone and deep copy code!
That’s not true, the existing method signatures of ConvertTo<T> have not changed which returns the same T generic type as before, so you can use the exact same client code as before.
Not according to this code example here: [Code Example][1]
When I do:
child.ConvertTo<Parent>()
I expect to get a < Parent> returned, yet I get a < Child> returned
We only saw it in the case were our web developer had inherited the DTO type in his viewmodels, and then was using ConvertTo to go back to the DTO for POSTing. The new behavior simply returns the original object back if you are trying to convert to the parent type.
No it doesn’t, generic methods that return T always return the Type of T as specified by the method signature. If T is a subclass of the instance it will return the instance casted to T.
So it does return Parent which you can verify with the equivalent code below without using Type inference:
Parent parent = child.ConvertTo<Parent>();
Calling GetType() returns the concrete type information, not the API return type that its currently cast to.
Again you can verify this by trying to call a child method on the parent instance:
public class Child : Parent
{
public void Hi() => Console.WriteLine("Hi");
}
var child = new Child();
var parent = child.ConvertTo<Parent>();
parent.Hi();
Which will fail with:
(17,8): error CS1061: ‘Parent’ does not contain a definition for ‘Hi’ and no accessible extension method ‘Hi’ accepting a first argument of type ‘Parent’ could be found (are you missing a using directive or an assembly reference?)
It’s returning the same instance because Parent is already an instance of Child, so no conversion is necessary.
The behavior you’re describing is addressed in my 2nd comment, The Convert<T> API does not convert types it doesn’t have to convert, the comment also shows different cloning APIs you can use if you’re instead looking for shallow or deep copy semantics.
Look like you’ve retracted/edited your original questions which now makes my answers sound like they’re answering a different question, and not your original one I answered.
Anyway, nothing is new, everything I’ve described has already been addressed.
Yes! I see. ConvertTo acts more like an implicit converter (not wanting to possibly lose information), while I was expecting more of a type coercion (or a cast) converter (if you lose information, you lose information).
Thank you for the clarifications and proper workaround. Perhaps we can bake this into ServiceStack to account for these edge cases?
public static T CastTo<T>(this object from)
{
T to = from.ConvertTo<T>();
return to.GetType() == typeof(T)
? to
: (T)typeof(T).CreateInstance().PopulateWithNonDefaultValues(from);
}
Ahh I C… so it’s because the ConvertTo when converting a subtype to a supertype, uses the same reference in memory, which is the subtype, and thus there is no way to coerce it into its supertype without creating a new instance as the supertype.
That’s what casting and what ConvertTo<T> does when it’s a subtype of T, it returns the same instance casted to the target T Type. But you’re still getting the original instance returned, not a different instance which would then be a shallow copy not a cast.
I’m sorry I have to open this thread again, but I ran into a similar problem now.
In my case, both classes (Child and Parent) have a route attribute, because I use them as some kind of internal proxy.
Yes, I know about the ProxyFeature, but I cannot use it here, because I have to do some special stuff before proxying the request.
After doing a
var parent = child.ConvertTo<Parent>();
and then
parent.ToGetUrl();
it still returns the Route of the Child class.
I think this is, because ToGetUrl() calls ToUrl internally, which then uses GetType() to determine the request type - which still returns Child.
Just wanted to mention this.
I will change my code now to use CreateCopy() instead, so I have a “real” instance of Parent.