Spotkałem się ostatnio z następującym problemem: Mam bibliotekę z modelami, oraz drugą opisującą Dto (Data Transfer Object), w której typy danych są bardzo zbliżone do modeli, ale są to mimo wszystko inne typy. Szukając rozwiązania, trafiłem na Mapster, bibliotekę do mapowania typów.
Użycie Mapstera jest dość proste:
using Mapster;
...
var result = TypeAdapter.Adapt<NewType>(original);
I w większości przypadków to w zupełności wystarcza. Ale są też bardziej złożone przypadki.
IEnumerable
Kiedy pod interfejsem IEnumerable
siedzi nie kolekcja, a iterator albo wyrażenie LINQ, to można dostać po twarzy wyjątkiem
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
cannot invoke non-delegate type
Rozwiązanie jest dość proste. Trzeba użyć .ToList()
lub .ToArray()
, aby przekształcić IEnumerable na prostą kolekcję.
Generyki
Niech się przypadkiem stanie, że nasz model to
class Model
{
public object Value { get; set; }
}
a typ na który go mapujemy to
class Model<T>
{
public T Value { get; set; }
}
Teraz nie jesteśmy w stanie uruchmić TypeAdapter.Adapt<>
, bo nie znamy T
w czasie kompilacji. Trzeba się wtedy odwołać do Refleksji.
typeof(TypeAdapter).GetMethod("Adapt", new[] { typeof(object) }) //get Adapt<>(object)
.MakeGenericMethod(
typeof(Model<>).MakeGenericType(
model.Value.GetType()))
.Invoke(null, model); //returns Model<T>, with the right type
//Note: Invoke gets null, because we invoke a static method, otherwise it'd be an object instance
Nie wygląda to bardzo pięknie, a gdy mamy np. IEnumerable<Model<T>>
to jest jeszcze więcej zabawy, ale działa.