Open sandboxFocusImprove this doc

LA0278: Constructor With Dependencies Clashes With Another Constructor

Severity Error
Reported by PostSharp
Message The constructor "<Constructor>" of "<Type>" cannot receive the dependencies <Dependencies>: the constructor with the added parameters would have the signature of the constructor "<OtherConstructor>". Remove one of the two constructors.

Cause

When an aspect attempts to introduce a dependency with ImportDependencyAdviceInstance, and the class has no field or readable instance property of the service type, PostSharp transforms each instance constructor of the class and of its derived classes. A constructor that has no parameter of exactly the service type gets an enhanced constructor: its own parameters followed by one parameter for each missing dependency. The original constructor becomes a forwarder that calls it.

PostSharp reports LA0278 on <Constructor> when its enhanced constructor would have the parameter types of <OtherConstructor>, which the class already declares. <Dependencies> lists the types of the parameters that PostSharp would add. A typical case is a parameterless constructor next to a constructor that receives the service.

In the following example, the parameterless constructor of OrderService would become OrderService( IAuditLog ).

using System;
using System.Collections.Generic;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0278_Error;

public interface IAuditLog
{
    void Write( string message );
}

public sealed class ConsoleAuditLog : IAuditLog
{
    public void Write( string message ) => Console.WriteLine( message );
}

[PSerializable]
public sealed class AuditedAttribute : MethodLevelAspect, IAdviceProvider
{
    public IEnumerable<AdviceInstance> ProvideAdvices( object targetElement )
    {
        yield return new ImportDependencyAdviceInstance( typeof(IAuditLog) );
    }

    [OnMethodEntryAdvice]
    public static void OnEntry( [Declaration] MethodBase method, [ImportMember] IAuditLog auditLog )
    {
        auditLog.Write( $"Executing {method.Name}." );
    }
}

public class OrderService
{
    // With the IAuditLog parameter added, this constructor would be OrderService( IAuditLog ).
    public OrderService() : this( new ConsoleAuditLog() )
    {
    }

    public OrderService( IAuditLog auditLog )
    {
        auditLog.Write( "Creating the order service." );
    }

    [Audited]
    public void PlaceOrder( int orderId ) => Console.WriteLine( $"Placing order {orderId}." );
}

public static class Program
{
    public static void Main()
    {
        new OrderService().PlaceOrder( 1 );
    }
}
error LA0278: LA0278_Error.cs(38,12): The constructor "Errors.LA0278_Error.OrderService.#ctor" of "Errors.LA0278_Error.OrderService" cannot receive the dependencies Errors.LA0278_Error.IAuditLog: the constructor with the added parameters would have the signature of the constructor "Errors.LA0278_Error.OrderService.#ctor(Errors.LA0278_Error.IAuditLog)". Remove one of the two constructors.

Resolution

Remove one of the two constructors. Usually, remove the constructor that does not receive the service: the constructor that does receives the dependency of the aspect from its own parameter, and PostSharp does not change it. A container passes the service, and code that uses new passes it explicitly.

Declaring the missing parameters in <Constructor> does not help, because the constructor then has the parameter types of <OtherConstructor>, which C# rejects.

In the following example, only the constructor that receives the IAuditLog remains, and both the class and the aspect use the log that it receives.

using System;
using System.Collections.Generic;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0278_Fixed;

public interface IAuditLog
{
    void Write( string message );
}

public sealed class ConsoleAuditLog : IAuditLog
{
    public void Write( string message ) => Console.WriteLine( message );
}

[PSerializable]
public sealed class AuditedAttribute : MethodLevelAspect, IAdviceProvider
{
    public IEnumerable<AdviceInstance> ProvideAdvices( object targetElement )
    {
        yield return new ImportDependencyAdviceInstance( typeof(IAuditLog) );
    }

    [OnMethodEntryAdvice]
    public static void OnEntry( [Declaration] MethodBase method, [ImportMember] IAuditLog auditLog )
    {
        auditLog.Write( $"Executing {method.Name}." );
    }
}

public class OrderService
{
    // The parameterless constructor is removed: this constructor already receives the IAuditLog.
    public OrderService( IAuditLog auditLog )
    {
        auditLog.Write( "Creating the order service." );
    }

    [Audited]
    public void PlaceOrder( int orderId ) => Console.WriteLine( $"Placing order {orderId}." );
}

public static class Program
{
    public static void Main()
    {
        new OrderService( new ConsoleAuditLog() ).PlaceOrder( 1 );
    }
}
Creating the order service.
Executing PlaceOrder.
Placing order 1.

See Also

Reference

ImportDependencyAdviceInstance

Other Resources

Receiving Dependencies from the Constructors of the Target Class