3 minutes
[ASP.NET Core] TypeConverter zamiast IModelBinder
W ramach nauki .NET Core piszę sobie aplikację webową. Na jednej podstronie mam wybór tygodnia, aby pokazywać dane z wybranego tygodnia.
Użyłem do wyboru tygodnia standardowego znacznika HTML:
<input type="week">
Opis problemu
Ten znacznik przesyła dane w postaci 2020-W24
, czyli rok, a następnie numer tygodnia. W C# nie ma żadnej klasy czy struktury temu odpowiadającej. Dlatego postanowiłem napisać sobie własną klasę Week
.
Zawiera ona jeden konstruktor:
public Week(int year, int week)
{
Year = year;
WeekNr = week;
}
Przeciążyłem metodę ToString, aby zwracało informacje w formacie z kontrolki <input type="week">
public override string ToString()
{
return $"{Year}-W{WeekNr}";
}
Zawiera jeszcze kilka metod pomocniczych:
public DateTime GetFirstDayOfWeek()
public static Week GetCurrentWeek()
public Week GetNextWeek()
public Week GetPreviousWeek()
public string GetDaysInWeekPeriod()
Wszystko spoko działa :-) Tylko za każdym razem jak przesyłam dane z formularza, to muszę stringa z informacją o tygodniu i roku przekształcać na obiekt Week
. Chciałbym aby automatycznie to się robiło.
Ucząc się podstaw ASP.NET Core czytałem oczywiście o czymś taki jak bindowanie danych. Framework sam konwertuje teksty na odpowiedni typ, dzięki temu programista już nie musi się o to martwić. Skoro takie coś istnieje, to pomyślałem, że pewnie da się jakąś własną klasę napisać, która zamieni przychodzący tekst na obiekt Week
.
Zacząłem googlować custom model binding i trafiłem na artykuł Custom Model Binding in ASP.NET Core z oficjalnej dokumentacji. Jest tam napisane jak zrobić własne bindowanie do własnego modelu.
Już zacząłem to robić. Dodałem klasę, która implementowała interfejs IModelBinder
, ale na szczęście doczytałem artykuł do końca :-)
Disclaimer: Czasami zdarza mi się, że szybko chce coś zrobić i przeglądam tylko kod kopiuje go do siebie, a potem się denerwuje, że coś nie działa. Okazuje się, że coś pominę i nie doczytam… i nie działa.
Na końcu jest napisane:
Typically shouldn’t be used to convert a string into a custom type, a TypeConverter is usually a better option.
Użycie TypeConverter zamiast IModelBinder
I tak zrobiłem :-) I wszystko działa i wydaje się nawet, że jest to prostsze rozwiązanie. Co musiałem zrobić?
Dodać klasę WeekConverter
, która dziedziczy po klasie TypeConverter
:
public class WeekConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string isoWeek)
{
var result = isoWeek.Split("-W");
return new Week(Convert.ToInt32(result[0]), Convert.ToInt32(result[1]));
}
return base.ConvertFrom(context, culture, value);
}
}
Zaimplementowałem tylko 2 metody, których potrzebuję:
- CanConvertFrom
- ConvertFrom
TypeConverter konweruje w 2 strony ale mi tylko była potrzebna konwersja z typu string
do Week
.
Dodatkowo klasę Week
musiałem oznaczyć atrybutem:
[TypeConverter(typeof(WeekConverter))]
public class Week
W metodzie oczywiście zmieniłem typ na Week
+ parę drobnych poprawek i już :-) I tylko tyle :-) Wszystko już działa.
Z formularza na stronie dostaję od razu obiekt Week
, a nie string
.
Podsumowanie
Jeśli możesz używaj TypeConverter zamiast własnego bindowania modelu. Czytaj dokumentację i artykuły do końca, mogą się tam znajdować ważne informacje.