Wednesday, December 26, 2012

Implement GetHashCode() to track changed objects in C#

Let's say we pull information about Movies from remote XML and save it in our database. Class that represent this objects can look like:
 public class Movie  
 {  
     public int Id { get; set; }  
     public string Name { get; set; }      
     public int? Duration { get; set; }  
     public int? Director { get; set; }      
     public int[] Actors { get; set; }      
     public int[] Genres { get; set; }      
     public int[] Countries { get; set; }      
     public string PosterUrl { get; set; }  
     public double? Rate { get; set; }  
     public int? Votes { get; set; }  
     public double? Imdb { get; set; }  
     public int? ImdbVotes { get; set; }  
     public string Site { get; set; }  
     public string Limits { get; set; }  
     public DateTime? Date { get; set; }  
     public string Description { get; set; }  
     public string Trailers { get; set; }  
     public string Company { get; set; }      
 }  

Write operations are expensive if there are a lot of objects to be saved, so for performance reasons we should NOT save new object in database, if there is old object in database that is the same. So we need to extract object from databse and determine if new object differ from the old one. If objects are the same we DO NOT update it.

But how to determine that objects are the same?

Objects are differ if their contents differ. In our case we can clarify this as follows: Objects are differ if their properties are differ.

Let's override GetHashCode() and Equals() methods to define how to check equality of 2 Movie objects:
 public class Movie  
 {  
    public int Id { get; set; }  
    public string Name { get; set; }  
    public int? Duration { get; set; }  
    public int? Director { get; set; }  
    public int[] Actors { get; set; }  
    public int[] Genres { get; set; }  
    public int[] Countries { get; set; }  
    public string PosterUrl { get; set; }  
    public double? Rate { get; set; }  
    public int? Votes { get; set; }  
    public double? Imdb { get; set; }  
    public int? ImdbVotes { get; set; }  
    public string Site { get; set; }  
    public string Limits { get; set; }  
    public DateTime? Date { get; set; }  
    public string Description { get; set; }  
    public string Trailers { get; set; }  
    public string Company { get; set; }  
    
    public override int GetHashCode()  
    {  
       return HashCodeGenerator.Generate(Id, Name, Duration, Director, Actors, Genres, Countries, PosterUrl, Rate, Votes, Imdb, ImdbVotes, Site, Limits, Date, Description, Trailers, Company);  
    }  
    public override bool Equals(object obj)  
    {  
       return this.GetHashCode() == obj.GetHashCode();  
    }        
  }  

As you can see, we use custom class HashCodeGenerator to generate hash code from specified property values that we need to track.

Implementation of this class:
  public static class HashCodeGenerator  
  {  
     public static int Generate(params object[] objects)  
     {  
       unchecked  
       {  
         var offset = 13;  
         var compositionOffset = 7;  
         foreach (object obj in objects)  
         {  
           if (obj==null)  
             continue;  
           if (obj is Array)  
           {  
             var array = obj as Array;  
             foreach (var item in array)  
             {  
               offset = (offset * compositionOffset) + item.GetHashCode();  
             }  
           }  
           else  
           {  
             offset = (offset * compositionOffset) + obj.GetHashCode();  
           }            
         }            
         return offset;  
       }  
     }      
   }  

No comments:

Post a Comment