| 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
privatesetter 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