Skip to content

Attributed Configuration

webJose edited this page Mar 3, 2019 · 6 revisions

Attributed Configuration webJose's ObjectComparer

(Esta página en Español)

As stated in the home page, one way to customize per-property object comparison is to use special attributes in the data types and its properties.

IMPORTANT: This type of configuration is global. This means that configuration made via attributes is read and stored globally inside the scanner's data cache. Object comparer objects created after type registration (scanning) will use the values configured via this method. The configuration made via this method is overridable globally using fluent interface type configuration, and locally using fluent interface comparer configuration.

This library defines three attribute classes: IgnoreForComparisonAttribute, PropertyMapAttribute, and ScanForPropertyComparisonAttribute.

The attributes must be in place before a type is registered (scanned), and they come into effect once a type has been registered.

To register a type, you may invoke one of the Scanner.RegisterType() methods. Execute one call per type to register, and do this once per application life cycle. For example, the call may be in the Main() method of a console application, or in the OnStart() method of a Windows Service.

public static void Main(string[] args)
{
    //Generic method call.
    Scanner.RegisterType<PurchaseOrder>();
    //Non-generic method call.  They are the same thing.
    Scanner.RegisterType(typeof(PurchaseOrderVM));
}

IgnoreForComparisonAttribute Attribute

The IgnoreForComparisonAttribute attribute is used to ignore a property when objects of the data type are compared against an undefined, or otherwise unlisted number of types, or when compared against objects of the same type, or both.

It is very simple to use, and provides three possible scenarios via three possible values. The following example shows the attribute used in all possible scenarios.

public class PurchaseOrder
{
    //Ignore the property when purchase orders are compared to objects of different types.
    //It is the same as [IgnoreForComparison(IgnorePropertyOptions.IgnoreForOthers)].
    [IgnoreForComparison]
    public OrderStatus Status { get; set; }

    //Ignore the property when purchase orders are compared to other purchase orders.
    [IgnoreForComparison(IgnorePropertyOptions.IgnoreForSelf)]
    public DateTime? ModifiedOn { get; set; }

    //Ignore the property when purchase orders are compared against any other object.
    [IgnoreForComparison(IgnorePropertyOptions.IgnoreForAll)]
    public byte[] RowVersion { get; set; }
}

PropertyMapAttribute Attribute

The PropertyMapAttribute attribute is used to either ignore a property when an object of the data type is compared against another of a specific data type, or to map the property to a property in the target data type with a different name. It is also possible to specify a property with the same name if string coercion is requested and you need to specify format strings. This topic is not discussed here, but you can read all about it in the String Coercion wiki page.

It is once more, very simple to use and when mapping to a property, string coercion may be specified. Because a property map is tied to a specific target type, more property maps may be added, one for each target data type.

public class PurchaseOrder
{
    //Ignore the property when purchase orders are compared against purchase order viewmodels.
    [PropertyMap(typeof(PurchaseOrderVM), PropertyMapOperation.IgnoreProperty)]
    public long Id { get; set; }

    //Map the property to a property of a different name in the PurchaseOrderVM class.
    //Ignore the property when compared to objects of type CalcPurchaseOrder.
    [PropertyMap(typeof(PurchaseOrderVM), PropertyMapOperation.MapToProperty, nameof(PurchaseOrderVM.POTotal))]
    [PropertyMap(typeof(CalcPurchaseOrder), PropertyMapOperation.IgnoreProperty]
    public decimal Total { get; set; }

    //Coerce order submission time value to string so only the date part is used during comparison.
    [PropertyMap(typeof(PurchaseOrder), PropertyMapOperation.MapToProperty, nameof(SubmissionDate),
    ForceStringValue = true, FormatString = "yyyyMMdd", TargetFormatString = "yyyyMMdd")]
    public DateTime SubmissionDate { get; set; }
}

ScanForPropertyComparisonAttribute Attribute

The last attribute to discuss is the ScanForPropertyComparisonAttribute attribute. It is meant to save you from many calls to of the Scanner.RegisterType() method. Apply the ScanForPropertyComparisonAttribute attribute to each data type that will be involved in per-property object comparison. Then instead of calling the RegisterType() method multiple times, simply call the ScanAssembly() method just once. This will register any types marked with the attribute.

//Do this for every data type that will be involved in property comparison.
[ScanForPropertyComparison]
public class PurchaseOrder { ... }
public static void Main(string[] args)
{
    //The PurchaseOrder class is used here just to anchor the assembly containing the type, assuming
    //all other types to be scanned are in that same assembly.
    Scanner.ScanAssembly(typeof(PurchaseOrder).Assembly);
    //If the project is simple enough and all data types to be scanned are in the same project (assembly),
    //then you may also do this:
    Scanner.ScanAssembly(Assembly.GetExecutingAssembly());
}

Available Languages

Clone this wiki locally