[ASP.NET Core] TypeConverter zamiast IModelBinder

Opublikowane przez Tomasz Prasołek w dniu

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.

Jak pewnie zauważyłeś, blog jest o systemie kontroli wersji Git, a ja tutaj piszę na całkowicie inny temat. O co kaman? A no o to, że chciałem napisać posta o tym, czego się ostatnio nauczyłem i go od razu opublikować. Skoro mam już bloga to dlaczego nie wykorzystać go od razu. Później – w wolnej chwili – raczej założę nowego bloga pod różne treści programistyczne, nie tylko związane z gitem.

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.

Photo by Suzanne D. Williams on Unsplash


2 Komentarze

Sinior · 25 czerwca 2020 o 15 h 57 min

Informacyjnie – wygląda na to, że na dzień dzisiejszy TypeConverter nie jest wspierany dla właściwości modeli: https://github.com/dotnet/aspnetcore/issues/8846#issuecomment-476942866

Pozdrawiam

    Tomasz Prasołek · 29 czerwca 2020 o 17 h 55 min

    Nie wiedziałem. Dzięki za info.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *