C# HashHelpers

Date: 2016-03-19
using System.Globalization;
using System.Security.Cryptography;
using System.Text;

namespace Domain.Helpers
{
    public interface IComparableByHash
    {
        IEnumerable<object> GetHashDependencies();
    }

    public interface IHashComparer
    {
        bool IsEqualTo(IComparableByHash other);
    }

    public static class HashHelpers
    {
        public static IEnumerable<object> SelectDependencies(params object[] dependencies) => dependencies;
        public static string GetHash(this IComparableByHash src) => Sha1FromDependencies(src.GetHashDependencies());
        public static IHashComparer Compare(IComparableByHash src) => new HashComparer(src);

        public static string Sha1FromDependencies(IEnumerable<object> dependencies) => GetSHA1(string.Join(";", dependencies.Select(x => Convert.ToString(x, CultureInfo.InvariantCulture)?.Trim())));

        public static string GetMD5(string data) => GetHash(MD5.Create(), data);
        public static string GetSHA1(string data) => GetHash(SHA1.Create(), data);
        public static string GetSHA256(string data) => GetHash(SHA256.Create(), data);
        public static string GetSHA384(string data) => GetHash(SHA384.Create(), data);
        public static string GetSHA512(string data) => GetHash(SHA512.Create(), data);

        public static string GetSHA1Guid(string data)
        {
            // SHA1 = 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
            // Guid = {3f2504e0-4f89-11d3-9a0c-0305e82c3301}
            string sha1 = GetSHA1(data);
            string[] parts = new string[] {
                sha1.Substring(0,8),
                sha1.Substring(8,4),
                sha1.Substring(12,4),
                sha1.Substring(16,4),
                sha1.Substring(20,12)
            };
            return string.Join("-", parts);
        }

        public static string GetHash(HashAlgorithm algoritm, string data)
        {
            byte[] inputBytes = Encoding.UTF8.GetBytes(data);
            byte[] hashBytes = algoritm.ComputeHash(inputBytes);
            // Convert the byte array to hexadecimal string
            var sb = new StringBuilder();
            for (int i = 0; i < hashBytes.Length; i++)
            {
                sb.Append(hashBytes[i].ToString("x2")); // For uppercase use: X2
            }
            return sb.ToString();
        }

        private class HashComparer : IHashComparer
        {
            private readonly string sourceHash;

            public HashComparer(IComparableByHash item)
            {
                sourceHash = item?.GetHash();
            }

            public bool IsEqualTo(IComparableByHash other)
            {
                if (sourceHash == null || other == null) return false;
                return sourceHash == other?.GetHash();
            }
        }
    }
}
public class MyItem : IComparableByHash
{
    public Guid? Id { get; set; }
    public string Description { get;set; }
    public double Amount { get; set; }
    public int Priority { get; set; }

    IEnumerable<object> IComparableByHash.GetHashDependencies() => HashHelpers.SelectDependencies(Description, Amount, Priority);
}

itemsToImport.Select(y =>
{
    var existing = existingItems.FirstOrDefault(x => x.Id == y.Id);
    var comparer = HashHelpers.Compare(existing);
    
    existing ??= new MyItem();
    existing.Description = item.Description;
    existing.Amount = item.Amount;
    existing.Priority = item.Priority;
    
    if (comparer.IsEqualTo(existing)) return null;
    return existing;
})
.Where(x => x != null)
.ToList();
1170cookie-checkC# HashHelpers