jeudi 13 octobre 2022

Store information about class properties

I apologize for my previous post, I'll try to be more detailed here.

I've got a few DTO classes, containing 100+ properties which in turn have several attributes on them containing even more data related to the properties. For example:

    [JsonPropertyName("saldo")]
    [DisplayData("Saldo", Unit.Currency)]
    [Section(Section.Taxes)]
    public string saldo { get; set; }

Other properties might have more attributes and others might have fewer. When I fetch the data from the API I deserialize the stream into the specified DTO class. Once that finishes I do the reflection part in order to extract all the data.

    public async Task<IEnumerable<DTOPropertyInfo>> GetPropertiesInfoAsync(BaseDTO dto)
    {
        List<DTOPropertyInfo> info = new();
        List<DTOPropertyInfo> favorites = new();
        QueryType type = dto.ToQueryType();
        PropertyInfo[] properties = dto.GetType().GetProperties();

        for (int i = 0; i < properties.Length; i++)
        {
            PropertyInfo property = properties[i];
            DisplayDataAttribute display = 
                (DisplayDataAttribute)property.GetCustomAttribute(typeof(DisplayDataAttribute));
            if (display is null)
            {
                continue;
            }

            object value = property.GetValue(dto);

            if (value is BaseDTO[] dtos)
            {
                for (int j = 0; j < dtos.Length; j++)
                {
                    IEnumerable<DTOPropertyInfo> nestedInfo =
                        await GetPropertiesInfoAsync(dtos[j]);
                    info.Add(new()
                    {
                        DisplayName = $"{display.DisplayName} {j + 1}",
                        Value = nestedInfo,
                        Section = Section.SubSection
                    });
                }
                continue;
            }
            else if (value is string[] strings)
            {
                if (strings.Length == 0)
                {
                    continue;
                }

                List<DTOPropertyInfo> dtoProperties = new();
                for (int j = 0; j < strings.Length; j++)
                {
                    dtoProperties.Add(new()
                    {
                        DisplayName = (j + 1).ToString(),
                        Value = strings[j]
                    });
                }

                info.Add(new()
                {
                    DisplayName = display.DisplayName,
                    Value = dtoProperties,
                    Section = Section.SubSection
                });
                continue;
            }
            else if (property.PropertyType.IsArray)
            {
                //Only support arrays of type string[] and BaseDTO[]
                //If more needed, add them to the if-chain
                continue;
            }

            value = (value as string).FormatDisplayValue();
            if (string.IsNullOrEmpty(value.ToString()))
            {
                continue;
            }


            SectionAttribute section =
                (SectionAttribute)property.GetCustomAttribute(typeof(SectionAttribute));
            if (section is null)
            {
                section = (SectionAttribute)dto.GetType().GetCustomAttribute(typeof(SectionAttribute));
                if (section is null)
                {
                    continue;
                }
            }

            if (type is not QueryType.None)
            {
                string favorite = await storage.GetAsync($"{type}/{display.DisplayName}");
                if (!string.IsNullOrEmpty(favorite))
                {
                    favorites.Add(new()
                    {
                        DisplayName = favorite,
                        Value = value,
                        IsFavorite = true,
                        Section = section.Section,
                        Unit = display.Unit
                    });
                    continue;
                }
            }

            info.Add(new()
            {
                DisplayName = display.DisplayName,
                Value = value,
                Section = section.Section,
                Unit = display.Unit
            });
        }

        info.InsertRange(0, favorites);
        return info;
    }

DTOPropertyInfo looks like the following:

public readonly struct DTOPropertyInfo
{
    public readonly string DisplayName { get; init; }
    public readonly Section Section { get; init; }
    public readonly object Value { get; init; }
    public readonly bool IsFavorite { get; init; }
    public readonly Unit Unit { get; init; }

    public DTOPropertyInfo(string name, Section section, string value, bool favorite, Unit unit)
    {
        DisplayName = name;
        Section = section;
        Value = value;
        IsFavorite = favorite;
        Unit = unit;
    }
}

The app is built with .NET MAUI and I haven't really made any 'proper' tests, I've installed the app on several iOS phones (both old & newer ones). Same with Android phone (both old & newer ones) and they all behave the same. iOS has no problem at all when it does the reflection, everything is just instant. However, Android phones are really slow when they do the reflection part. Although newer Android phones do perform a little better than the old ones but the performance hit is still quite noticeable.

I guess I could just write out each and every property by hand but that would be very, very tedious.





Aucun commentaire:

Enregistrer un commentaire