Search code examples
c#.netsystem.reflectionvariance

Find the variance between two Objects in C#


I'm looking for a way to get the variance between two instances of an object. the following function I've written uses reflection to serve the purpose at hand, but i want to enhance it a bit more so it could skip certain fields with specific data annotations. For example the '[NotMapped] & [JsonIgnore]' annotation that is used in Entity Framework Models

    public static List<ChangeSet> GetVariances<T>(this T previous, T updated)
    {
        List<ChangeSet> changeSet = new List<ChangeSet>();
        try
        {
            string[] excludedFields = { "Id", "DateCreated", "DateModified" };
            Type entityType = previous.GetType();
            FieldInfo[] fieldInfo = entityType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
            foreach (FieldInfo x in fieldInfo)
            {
                if (!excludedFields.Any(z => x.Name.Contains(z))){
                    ChangeSet change = new ChangeSet
                    {
                        Field = x.Name,
                        PreviousValue = x.GetValue(previous),
                        UpdatedValue = x.GetValue(updated)
                    };
                    if (!Equals(change.PreviousValue, change.UpdatedValue))
                        changeSet.Add(change);
                }
            }
        }
        catch (Exception ex)
        {
            var exception = ex.Message;
        }
        return changeSet;
    }

Example of a model that is being used:

[Table("ClientMaster")]
public partial class ClientMaster
{
    [Key]
    [JsonProperty(PropertyName = "id")]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long Id { get; set; }

    [JsonProperty(PropertyName = "clientId")]
    [Required, StringLength(250)]
    public string ClientId { get; set; }

    [JsonProperty(PropertyName = "approvalLevel")]
    [Required, StringLength(200)]
    public string ApprovalLevel { get; set; }

    [NotMapped]
    [JsonProperty(PropertyName = "attachments")]
    public List<ClientAttachmentModel> Attachments { get; set; }

    [JsonIgnore]
    public virtual UserInformation CreatedByUser { get; set; }

    [JsonIgnore]
    public virtual UserInformation ModifiedByUser { get; set; }

    [JsonIgnore]
    public virtual ICollection<TaskMaster> TaskMaster { get; set; }
}

Could anyone please guide me to make the function skip certain data annotations. Any help appreciated.


Solution

  • If what you're after is just finding what attributes are decorating a particular property, you'll need to call GetProperties on your type, instead of GetFields as you currently are. This is because your attributes are decorating properties, and not fields. GetFields will retrieve the compiler-generated backing fields, which probably isn't what you're after. You can still perform your value comparisons in the same way.

    Now, to check for attributes, each PropertyInfo object in the array returned by GetProperties will have an array property CustomAttributes, which contains details of that property's decorating attributes and any arguments you've provided to those attributes. If you just want to check for the presence of an attribute and don't care about arguments, Magnus's solution achieves the same and quicker.

    (I'll also mention that in your code sample, using Name.Contains will mean that for example, ClientId will be skipped because its name contains Id, which you list as an excluded field. Name will, for properties you've declared, just return the name as declared, so you could just check for equality.)