| Severity | Error |
| Reported by | PostSharp |
| Message | The type "<SerializerType>" cannot be used as the serializer of the aspect "<Aspect>" because it has no public parameterless constructor. |
Cause
PostSharp serializes the aspect instances at build time and deserializes them at run time, as Understanding Aspect Serialization describes. You can choose the serializer of an aspect with the SerializerType property of an aspect configuration attribute, such as OnMethodBoundaryAspectConfigurationAttribute or AspectConfigurationAttribute, or with the SerializerType property of the aspect. The type must be derived from AspectSerializer.
PostSharp creates the serializer itself, at build time to serialize the aspects and at run time to deserialize them, so the type must have a public constructor without parameters. PostSharp reports LA0022 when it has none: each of its constructors has parameters or is not public. <SerializerType> is the configured type, and <Aspect> the aspect class. PostSharp reports the message on the serializer class.
PostSharp then also reports LA0001, with the message Object reference not set to an instance of an object, because it tries to serialize the aspects without a serializer.
In the following example, the only constructor of LogAspectSerializer takes an Encoding.
using System;
using System.IO;
using System.Text;
using PostSharp.Aspects;
using PostSharp.Aspects.Configuration;
using PostSharp.Aspects.Serialization;
using PostSharp.Reflection;
namespace Errors.LA0022_Error;
// Writes the category of each [Log] aspect at build time and reads it back at run time.
// PostSharp cannot create this serializer: its only constructor has a parameter.
public sealed class LogAspectSerializer : AspectSerializer
{
private readonly Encoding encoding;
public LogAspectSerializer( Encoding encoding )
{
this.encoding = encoding;
}
public override void Serialize( IAspect[] aspects, Stream stream, IMetadataEmitter metadataEmitter )
{
using BinaryWriter writer = new( stream, this.encoding, leaveOpen: true );
writer.Write( aspects.Length );
foreach ( IAspect aspect in aspects )
{
writer.Write( ((LogAttribute) aspect).Category );
}
}
protected override IAspect[] Deserialize( Stream stream, IMetadataDispenser metadataDispenser )
{
using BinaryReader reader = new( stream, this.encoding, leaveOpen: true );
IAspect[] aspects = new IAspect[reader.ReadInt32()];
for ( int i = 0; i < aspects.Length; i++ )
{
aspects[i] = new LogAttribute( reader.ReadString() );
}
return aspects;
}
}
[OnMethodBoundaryAspectConfiguration( SerializerType = typeof(LogAspectSerializer) )]
public class LogAttribute : OnMethodBoundaryAspect
{
public LogAttribute( string category )
{
this.Category = category;
}
public string Category { get; }
public override void OnEntry( MethodExecutionArgs args )
{
Console.WriteLine( $"[{this.Category}] Entering {args.Method.Name}." );
}
}
public class OrderService
{
[Log( "Orders" )]
public void PlaceOrder( string product )
{
Console.WriteLine( $"Order placed for {product}." );
}
}
public static class Program
{
public static void Main()
{
new OrderService().PlaceOrder( "Keyboard" );
}
}
error LA0022: LA0022_Error.cs(13,21): The type "Errors.LA0022_Error.LogAspectSerializer" cannot be used as the serializer of the aspect "Errors.LA0022_Error.LogAttribute" because it has no public parameterless constructor.
Resolution
- Add a public parameterless constructor to the serializer class. Initialize in this constructor the settings that the other constructors receive, or read them from the aspects themselves.
- Unless the aspect needs a serializer of its own, remove the
SerializerTypeproperty and annotate the aspect class with PSerializableAttribute.
In the following example, LogAspectSerializer has a public parameterless constructor, which uses the UTF-8 encoding.
using System;
using System.IO;
using System.Text;
using PostSharp.Aspects;
using PostSharp.Aspects.Configuration;
using PostSharp.Aspects.Serialization;
using PostSharp.Reflection;
namespace Errors.LA0022_Fixed;
// Writes the category of each [Log] aspect at build time and reads it back at run time.
public sealed class LogAspectSerializer : AspectSerializer
{
private readonly Encoding encoding;
// PostSharp creates the serializer with this public parameterless constructor.
public LogAspectSerializer()
{
this.encoding = Encoding.UTF8;
}
public override void Serialize( IAspect[] aspects, Stream stream, IMetadataEmitter metadataEmitter )
{
using BinaryWriter writer = new( stream, this.encoding, leaveOpen: true );
writer.Write( aspects.Length );
foreach ( IAspect aspect in aspects )
{
writer.Write( ((LogAttribute) aspect).Category );
}
}
protected override IAspect[] Deserialize( Stream stream, IMetadataDispenser metadataDispenser )
{
using BinaryReader reader = new( stream, this.encoding, leaveOpen: true );
IAspect[] aspects = new IAspect[reader.ReadInt32()];
for ( int i = 0; i < aspects.Length; i++ )
{
aspects[i] = new LogAttribute( reader.ReadString() );
}
return aspects;
}
}
[OnMethodBoundaryAspectConfiguration( SerializerType = typeof(LogAspectSerializer) )]
public class LogAttribute : OnMethodBoundaryAspect
{
public LogAttribute( string category )
{
this.Category = category;
}
public string Category { get; }
public override void OnEntry( MethodExecutionArgs args )
{
Console.WriteLine( $"[{this.Category}] Entering {args.Method.Name}." );
}
}
public class OrderService
{
[Log( "Orders" )]
public void PlaceOrder( string product )
{
Console.WriteLine( $"Order placed for {product}." );
}
}
public static class Program
{
public static void Main()
{
new OrderService().PlaceOrder( "Keyboard" );
}
}
[Orders] Entering PlaceOrder.
Order placed for Keyboard.
See Also
Reference
AspectSerializer
OnMethodBoundaryAspectConfigurationAttribute
Other Resources