| Severity | Error |
| Reported by | PostSharp |
| Message | The aspect "<Aspect>" applied to "<Target>" attempts to introduce a dependency of type "<DependencyType>" in "<TargetType>", and several existing properties of this type could provide it instead: <Properties>. Remove all but one of these properties, or give the class a field of type "<DependencyType>" that stores the dependency. |
Cause
When an aspect attempts to introduce a dependency with an ImportDependencyAdviceInstance, PostSharp first looks for a member of <TargetType> that already holds the dependency:
- A field of the dependency type, declared by the class, or by a base class when the class can access it.
- A property that PostSharp created for the same dependency of a base class.
- A readable instance property of the dependency type, without parameters, declared by the class, or by a base class when the class can access its getter. The class initializes this property itself, so PostSharp uses it and does not add any parameter to the constructors.
When none of these members exists, PostSharp introduces a field and adds a parameter of the dependency type to the constructors. PostSharp reports LA0286 when step 3 finds several properties: it cannot choose which one provides the dependency. <Properties> lists these properties, and <Target> is the declaration to which the aspect is applied, where the error is located. LA0286 is always an error.
PostSharp looks for these members by their type only. The Name of the request does not change the search: it is the name by which the advices import the dependency.
When the advice imports the dependency by its type, with ImportMemberAttribute, the same properties also make this import ambiguous, so PostSharp also reports LA0283 for the advice parameter. Both errors disappear with the same fix.
In the following example, OrderService has two properties of type IAuditLog, and the [Audited] aspect attempts to introduce a dependency of this type.
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;
namespace Errors.LA0286_Error;
public interface IAuditLog
{
void Write( string message );
}
public sealed class ConsoleAuditLog : IAuditLog
{
private readonly string name;
public ConsoleAuditLog( string name )
{
this.name = name;
}
public void Write( string message ) => Console.WriteLine( $"[{this.name}] {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
{
public IAuditLog AuditLog { get; }
// A second property of type IAuditLog makes the dependency ambiguous.
public IAuditLog ErrorLog { get; }
public OrderService( IAuditLog auditLog, IAuditLog errorLog )
{
this.AuditLog = auditLog;
this.ErrorLog = errorLog;
}
[Audited]
public void PlaceOrder( int orderId ) => Console.WriteLine( $"Placing order {orderId}." );
}
public static class Program
{
public static void Main()
{
new OrderService( new ConsoleAuditLog( "audit" ), new ConsoleAuditLog( "error" ) ).PlaceOrder( 1 );
}
}
error LA0283: LA0286_Error.cs(57,17): The parameter "auditLog" of the advice "Errors.LA0286_Error.AuditedAttribute.OnEntry(System.Reflection.MethodBase, Errors.LA0286_Error.IAuditLog)" is bound by [ImportMember] to a member of its type, but several members of "Errors.LA0286_Error.OrderService" match: AuditLog, ErrorLog. Specify the name of the member.
error LA0286: LA0286_Error.cs(57,17): The aspect "Errors.LA0286_Error.AuditedAttribute" applied to "Errors.LA0286_Error.OrderService.PlaceOrder(System.Int32)" attempts to introduce a dependency of type "Errors.LA0286_Error.IAuditLog" in "Errors.LA0286_Error.OrderService", and several existing properties of this type could provide it instead: "AuditLog", "ErrorLog". Remove all but one of these properties, or give the class a field of type "Errors.LA0286_Error.IAuditLog" that stores the dependency.
Resolution
- Keep only one property of the dependency type, so that this property provides the dependency.
- Otherwise, declare a field of the dependency type in the class, and assign it the dependency. PostSharp uses this field before it looks at the properties.
In the following example, ErrorLog is a TextWriter, so AuditLog is the only property of type IAuditLog, and the advice receives its value.
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;
namespace Errors.LA0286_Fixed;
public interface IAuditLog
{
void Write( string message );
}
public sealed class ConsoleAuditLog : IAuditLog
{
private readonly string name;
public ConsoleAuditLog( string name )
{
this.name = name;
}
public void Write( string message ) => Console.WriteLine( $"[{this.name}] {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 only property of type IAuditLog provides the dependency.
public IAuditLog AuditLog { get; }
public TextWriter ErrorLog { get; }
public OrderService( IAuditLog auditLog, TextWriter errorLog )
{
this.AuditLog = auditLog;
this.ErrorLog = errorLog;
}
[Audited]
public void PlaceOrder( int orderId ) => Console.WriteLine( $"Placing order {orderId}." );
}
public static class Program
{
public static void Main()
{
new OrderService( new ConsoleAuditLog( "audit" ), Console.Error ).PlaceOrder( 1 );
}
}
[audit] Executing PlaceOrder.
Placing order 1.
See Also
Reference
ImportDependencyAdviceInstance
ImportMemberAttribute
Other Resources
Receiving Dependencies from the Constructors of the Target Class