| Severity | Warning |
| Reported by | PostSharp |
| Message | The CompatibilityLevel property has the invalid value '<Value>', which PostSharp ignores. Set it to a version with at least a major and a minor number, such as 2027.0, usually with the MSBuild property PostSharpCompatibilityLevel. |
Cause
The compatibility level is the version of PostSharp whose behavior PostSharp keeps where a later version changed it. You usually set it with the PostSharpCompatibilityLevel MSBuild property, which PostSharp receives as its CompatibilityLevel property; see Well-Known MSBuild Properties. PostSharp reads the value only when a behavior depends on it, for example the path of an exception thrown by the OnYield or OnResume advice of an async method.
PostSharp reports PS0261 when the value is not a version in the sense of Version, with at least a major and a minor number. A year alone, such as 2027, is not a valid version. PostSharp then ignores the value and behaves as if the property were not set. The message has no source location.
In the following example, the compatibility level is 2027, and the [Profile] aspect has OnYield and OnResume advices on an async method. Because PostSharp ignores the value, the project behaves as if it set no compatibility level, so PostSharp also reports LA0250 on the method.
// The compatibility level is not a version: a version has at least a major and a minor number.
// @Property(CompatibilityLevel=2027)
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Serialization;
namespace Errors.PS0261_Error;
// Measures the time that a method spends running, without the time it spends awaiting.
[PSerializable]
public class ProfileAttribute : OnMethodBoundaryAspect
{
[PNonSerialized]
private Stopwatch? stopwatch;
public override void OnEntry( MethodExecutionArgs args ) => this.stopwatch = Stopwatch.StartNew();
public override void OnYield( MethodExecutionArgs args ) => this.stopwatch?.Stop();
public override void OnResume( MethodExecutionArgs args ) => this.stopwatch?.Start();
public override void OnExit( MethodExecutionArgs args )
{
this.stopwatch?.Stop();
Console.WriteLine( $"{args.Method.Name} completed." );
}
}
public class OrderService
{
[Profile]
public async Task PlaceOrderAsync( string product )
{
await Task.Delay( 10 );
Console.WriteLine( $"Ordered {product}." );
}
}
public static class Program
{
public static void Main()
{
new OrderService().PlaceOrderAsync( "Coffee" ).GetAwaiter().GetResult();
}
}
warning LA0250: PS0261_Error.cs(35,23): An exception thrown by the OnYield or OnResume advice of the aspect "Errors.PS0261_Error.ProfileAttribute" in the async method "Errors.PS0261_Error.OrderService.PlaceOrderAsync(System.String)" follows the behavior of PostSharp 2026.0: it skips the catch and finally blocks around the await and the OnExit advices. Set the MSBuild property PostSharpCompatibilityLevel to 2027.0 to let it flow as an exception of the await expression, or to 2026.0 to keep this behavior without this warning.
warning PS0261: The CompatibilityLevel property has the invalid value '2027', which PostSharp ignores. Set it to a version with at least a major and a minor number, such as 2027.0, usually with the MSBuild property PostSharpCompatibilityLevel.
Resolution
Set the compatibility level to a version with a major and a minor number, such as 2027.0:
<PropertyGroup>
<PostSharpCompatibilityLevel>2027.0</PostSharpCompatibilityLevel>
</PropertyGroup>
When the property is set in the PostSharpProperties MSBuild property or in another place, correct the value there.
In the following example, the compatibility level is 2027.0, and PostSharp reports neither PS0261 nor LA0250.
// The compatibility level is a version with a major and a minor number.
// @Property(CompatibilityLevel=2027.0)
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Serialization;
namespace Errors.PS0261_Fixed;
// Measures the time that a method spends running, without the time it spends awaiting.
[PSerializable]
public class ProfileAttribute : OnMethodBoundaryAspect
{
[PNonSerialized]
private Stopwatch? stopwatch;
public override void OnEntry( MethodExecutionArgs args ) => this.stopwatch = Stopwatch.StartNew();
public override void OnYield( MethodExecutionArgs args ) => this.stopwatch?.Stop();
public override void OnResume( MethodExecutionArgs args ) => this.stopwatch?.Start();
public override void OnExit( MethodExecutionArgs args )
{
this.stopwatch?.Stop();
Console.WriteLine( $"{args.Method.Name} completed." );
}
}
public class OrderService
{
[Profile]
public async Task PlaceOrderAsync( string product )
{
await Task.Delay( 10 );
Console.WriteLine( $"Ordered {product}." );
}
}
public static class Program
{
public static void Main()
{
new OrderService().PlaceOrderAsync( "Coffee" ).GetAwaiter().GetResult();
}
}
Ordered Coffee.
PlaceOrderAsync completed.
See Also
Reference
OnMethodBoundaryAspect
ICompatibilityLevelService
Other Resources
Well-Known MSBuild Properties
Semantic Advising of Iterator and Async Methods