Open sandboxFocusImprove this doc

LA0214: Binding Used Twice in One Advice

Severity Error
Reported by PostSharp
Message The parameter "<Parameter>" of the advice "<Advice>" is bound by <Binding>, but the advice already has a parameter bound by <Binding>.

Cause

An advice can have at most one parameter annotated with ExceptionAttribute and one annotated with ReturnValueAttribute, because the exception and the return value are single values. PostSharp reports LA0214 on the second such parameter of the advice. <Binding> is the binding attribute, [Exception] or [ReturnValue].

Most other bindings can be used several times in one advice, for instance to bind two arguments. Several [FlowBehavior] or [Awaiter] parameters cause the warning LA0236 instead.

In the following example, the exception advice has one [Exception] parameter to read the exception and another one to replace it. Because the reported parameter is an out parameter, PostSharp reports the message without a file and line, but the message names the parameter and the advice.

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

namespace Errors.LA0214_Error;

public class DataAccessException : Exception
{
    public DataAccessException( string message, Exception innerException )
        : base( message, innerException )
    {
    }
}

[PSerializable]
public class WrapExceptionsAttribute : MethodLevelAspect
{
    [OnMethodExceptionAdvice]
    public static void OnException(
        [Exception] Exception exception,
        // A second [Exception] parameter cannot receive the replacement exception.
        [Exception] out Exception replacement,
        [FlowBehavior] out FlowBehavior flowBehavior )
    {
        replacement = new DataAccessException( "The database could not be read.", exception );
        flowBehavior = FlowBehavior.ThrowException;
    }
}

public class CustomerRepository
{
    [WrapExceptions]
    public string GetName( int id ) => throw new InvalidOperationException( $"Customer {id} not found." );
}

public static class Program
{
    public static void Main()
    {
        try
        {
            new CustomerRepository().GetName( 42 );
        }
        catch ( DataAccessException e )
        {
            Console.WriteLine( $"{e.Message} Inner exception: {e.InnerException!.Message}" );
        }
    }
}
error LA0214: LA0214_Error.cs(23,35): The parameter "replacement" of the advice "Errors.LA0214_Error.WrapExceptionsAttribute.OnException(System.Exception, System.Exception@, PostSharp.Aspects.FlowBehavior@)" is bound by [Exception], but the advice already has a parameter bound by [Exception].

Resolution

Keep one parameter with the binding. To read a value and replace it, declare this parameter as ref: an [Exception] parameter passed as ref receives the exception, and the value that the advice assigns to it is the exception that FlowBehavior.ThrowException throws. A [ReturnValue] parameter passed as ref works the same way in the advices that accept it. See Handling exceptions.

In the following example, a single ref parameter receives the exception and replaces it with a DataAccessException.

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

namespace Errors.LA0214_Fixed;

public class DataAccessException : Exception
{
    public DataAccessException( string message, Exception innerException )
        : base( message, innerException )
    {
    }
}

[PSerializable]
public class WrapExceptionsAttribute : MethodLevelAspect
{
    [OnMethodExceptionAdvice]
    public static void OnException(
        // A single ref parameter reads the exception and replaces it.
        [Exception] ref Exception exception,
        [FlowBehavior] out FlowBehavior flowBehavior )
    {
        exception = new DataAccessException( "The database could not be read.", exception );
        flowBehavior = FlowBehavior.ThrowException;
    }
}

public class CustomerRepository
{
    [WrapExceptions]
    public string GetName( int id ) => throw new InvalidOperationException( $"Customer {id} not found." );
}

public static class Program
{
    public static void Main()
    {
        try
        {
            new CustomerRepository().GetName( 42 );
        }
        catch ( DataAccessException e )
        {
            Console.WriteLine( $"{e.Message} Inner exception: {e.InnerException!.Message}" );
        }
    }
}
The database could not be read. Inner exception: Customer 42 not found.

See Also

Reference

ExceptionAttribute
ReturnValueAttribute

Other Resources

Advice Parameters