Attributes Thoughts

June 25, 2022

Attributes are used in Acumatica heavily and significant amount of functionality is expressed through attributes. In many cases, there usage is overcomplicated.
Let’s look for example at the usage of the PXStringList attribute:


    [PXStringList(
    new string[]
    {
        WorkOrderStatusConstants.OnHold,
        WorkOrderStatusConstants.PendingPayment,
        WorkOrderStatusConstants.ReadyForAssignment,
        WorkOrderStatusConstants.Assigned,
        WorkOrderStatusConstants.Completed,
        WorkOrderStatusConstants.Paid
    },
    new string[]
    {
        Messages.OnHold,
        Messages.PendingPayment,
        Messages.ReadyForAssignment,
        Messages.Assigned,
        Messages.Completed,
        Messages.Paid
    })]
    public virtual string Status { get; set; }

In case of usage of PXSelector things are even worse:


    [PXSelector(typeof(Search<RSSVRepairService.serviceID>),
        typeof(RSSVRepairService.serviceCD),
        typeof(RSSVRepairService.description),
        SubstituteKey = typeof(RSSVRepairService.serviceCD),
        DescriptionField = typeof(RSSVRepairService.description))]
    public virtual int? ServiceID { get; set; }

This code is not good from readability perspective. More important it is quite problematic to debug, extend, and maintain it.
Surprisingly we can see code like this not just in training examples but in Acumatica itself.

Would it be nice to keep it simple, just like that:


    [Status]
    public virtual string Status { get; set; }

Luckily, we can achieve it easily.
Attribute is just a C# class. So, all we need to do is to create our own attribute inherited from that problematic one and use its proper constructor. For example, to replace PXStringList we can implement our own Status attribute as the following:


    using PX.Data;
    namespace PhoneRepairShop
    {
        public class StatusAttribute : PXStringListAttribute
        {
            private static readonly string[] allowedValues = new string[]
            {
                WorkOrderStatusConstants.OnHold,
                WorkOrderStatusConstants.PendingPayment,
                WorkOrderStatusConstants.ReadyForAssignment,
                WorkOrderStatusConstants.Assigned,
                WorkOrderStatusConstants.Completed,
                WorkOrderStatusConstants.Paid,
                WorkOrderStatusConstants.Shipped
            };

            private static readonly string[] allowedLabels = new string[]
            {
                Messages.OnHold,
                Messages.PendingPayment,
                Messages.ReadyForAssignment,
                Messages.Assigned,
                Messages.Completed,
                Messages.Paid,
                Messages.Shipped
            };
        };

            public StatusAttribute() : base(allowedValues, allowedLabels) { }
        }
    }

Et voila, now our code is more readable, and we can easily debug, extend, and maintain it!

We can use similar approach to improve usage of PXSelector attribute as well.