Open sandboxFocusImprove this doc

LA0158: Validation Advice Applied to a Read-Only Property

Severity Error
Reported by PostSharp
Message LocationValidationAdvice cannot be applied to property "<Property>" because it is read-only.

Cause

A method of an aspect annotated with LocationValidationAdvice validates the values assigned to a field, a property or a parameter. For a property, PostSharp inserts the validation into the setter. When the property has no setter, PostSharp has no place to insert it and reports LA0158.

A get-only auto-property such as public decimal Balance { get; } has no setter: its constructor assigns the backing field directly, so PostSharp cannot validate the value either.

In the following example, the [NonNegative] aspect validates the Balance property of Account, which has only a getter.

using System;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Extensibility;
using PostSharp.Reflection;
using PostSharp.Serialization;

namespace Errors.LA0158_Error;

[PSerializable]
[MulticastAttributeUsage( MulticastTargets.Property )]
public class NonNegativeAttribute : Aspect
{
    [LocationValidationAdvice]
    public Exception? ValidateValue( decimal value, string locationName, LocationKind locationKind, LocationValidationContext context )
    {
        return value < 0 ? new ArgumentOutOfRangeException( locationName, "The value cannot be negative." ) : null;
    }
}

public class Account
{
    public Account( decimal balance )
    {
        this.Balance = balance;
    }

    // The property has no setter, so the aspect cannot validate the values assigned to it.
    [NonNegative]
    public decimal Balance { get; }
}

public static class Program
{
    public static void Main()
    {
        Console.WriteLine( new Account( 100 ).Balance );

        try
        {
            _ = new Account( -5 );
        }
        catch ( ArgumentOutOfRangeException e )
        {
            Console.WriteLine( e.Message );
        }
    }
}
error LA0158: LA0158_Error.cs(30,20): LocationValidationAdvice cannot be applied to property "Errors.LA0158_Error.Account.Balance" because it is read-only.

Resolution

Choose one of the following fixes:

  • Add a setter to the property. A private setter is enough: the validation runs each time the setter is called, also from the constructor.
  • Validate the parameter of the constructor or of the method that receives the value, with an aspect that can target parameters, instead of the property.
  • Remove the aspect from the property.

In the following example, Balance has a private setter, so the aspect rejects the negative balance passed to the constructor.

using System;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Extensibility;
using PostSharp.Reflection;
using PostSharp.Serialization;

namespace Errors.LA0158_Fixed;

[PSerializable]
[MulticastAttributeUsage( MulticastTargets.Property )]
public class NonNegativeAttribute : Aspect
{
    [LocationValidationAdvice]
    public Exception? ValidateValue( decimal value, string locationName, LocationKind locationKind, LocationValidationContext context )
    {
        return value < 0 ? new ArgumentOutOfRangeException( locationName, "The value cannot be negative." ) : null;
    }
}

public class Account
{
    public Account( decimal balance )
    {
        this.Balance = balance;
    }

    // The private setter gives the aspect a place to validate the value.
    [NonNegative]
    public decimal Balance { get; private set; }
}

public static class Program
{
    public static void Main()
    {
        Console.WriteLine( new Account( 100 ).Balance );

        try
        {
            _ = new Account( -5 );
        }
        catch ( ArgumentOutOfRangeException e )
        {
            Console.WriteLine( e.Message );
        }
    }
}
100
The value cannot be negative. (Parameter 'Balance')

See Also

Reference

LocationValidationAdvice
ILocationValidationAspect<T>

Other Resources

Advice Methods
Creating Custom Contracts