C# Compare Two Objects and Get Differences

Db de kayıtlı bulunan bir nesne için Update işlemi yaparken bazen öyle denk gelir ki sizden sadece ilgili objede değişen alanların gönderilmesi veya değişen alan var mı yok mu diye bir takım can sıkıcı validasyonlar yapmanız istenir ki hele bir de işin içine javascript girerse yemede yanında yat. İşte bu gibi durumlar için gidip sürekli olarak mevcut objedeki alanla yeni değer compare edilip değişmiş mi değişmemiş mi diye bir sürü logic yazmayı tabii ki de pek istemeyiz.

Örnek bir case üzerinden anlatmak gerekirse; aşağıdaki gibi User diye bir modeliniz ve sizden UpdateUser adından bir end-point yazmanız isteniyor ve analizde objede bulunan hangi alan kullanıcı tarafından değiştirilmiş bunlarla ilgili log tutmanız veya bilgilendirme emaili atmanız gerektiği yazılmış.

    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string EMail { get; set; }
        public DateTime BirthDate { get; set; }
        public int ZipCode { get; set; }
        public bool IsPublic { get; set; }
    }

Gidip db de kayıtlı bulunan objedeki property ile kullanıcıdan aldığınız değeri teker teker compare etmek istemeyiz elbette  if(dbUserModel.FirstName == newUserModel.FirstName) . Bu gibi bir durum için Reflection'dan faydalanabiliriz. Aşağıdaki gibi bir MemberComparisonResult adında bir model tanımlayalım ve generic tanımlayacağımız CompareObjectsAndGetDifferences  adından ki metottan bu modeli döndürelim.

    public class MemberComparisonResult
    {
        public string Name { get; }
        public object FirstValue { get; }
        public object SecondValue { get; }

        public MemberComparisonResult(string name, object firstValue, object secondValue)
        {
            Name = name;
            FirstValue = firstValue;
            SecondValue = secondValue;
        }

        public override string ToString()
        {
            return Name + " : " + FirstValue.ToString() + (FirstValue.Equals(SecondValue) ? " == " : " != ") + SecondValue.ToString();
        }
    }

Şimdi ise CompareObjectsAndGetDifferences adındaki metodumuzu aşağıdaki yazalım.

        public static List<MemberComparisonResult> CompareObjectsAndGetDifferences<T>(T firstObj, T secondObj)
        {
            var differenceInfoList = new List<MemberComparisonResult>();

            foreach (var member in typeof(T).GetMembers())
            {
                if (member.MemberType == MemberTypes.Property)
                {
                    var property = (PropertyInfo)member;
                    if (property.CanRead && property.GetGetMethod().GetParameters().Length == 0)
                    {
                        var xValue = property.GetValue(firstObj, null);
                        var yValue = property.GetValue(secondObj, null);
                        if (!object.Equals(xValue, yValue))
                            differenceInfoList.Add(new MemberComparisonResult(property.Name, xValue, yValue));
                    }
                    else
                        continue;
                }
            }
            return differenceInfoList;
        }
    }

Son olarak da yazdığımız metodu aşağıdaki gibi bir ConsoleApplication'da test edelim.

        static void Main(string[] args)
        {
            var existingUser = new User
            {
                FirstName = "Mestan",
                LastName = "Tosuner",
                BirthDate = DateTime.Today.AddYears(23),
                EMail = "mestan@mestanali.com",
                ZipCode = 34000,
                IsPublic = false
            };

            var existingUserWithNewValue = new User
            {
                FirstName = "Mestan Ali",
                LastName = "Tosuner",
                BirthDate = DateTime.Today.AddYears(23),
                EMail = "mestan@mestanali.com",
                ZipCode = 55400,
                IsPublic = true
            };

            var changes = CompareObjectsAndGetDifferences(existingUser, existingUserWithNewValue);
            foreach (var item in changes)
            {
                Console.WriteLine(item.ToString());
            }
        }

Projenizi çalıştırdıktan sonra ekran çıktısı aşağıdaki gibi olacaktır.

Gördüğünüz gibi existingUserWithNewValue ismindeki modeldeki değerler kullanıcıdan yeni alınan değerlere sahip. Yukarıdaki ekran görüntüsünde de bize FirstName, ZipCode ve IsPublic alanlarının güncellendiğini söylemekte ve ilk değerleri ile ikinci değerlerini ekrana display etmekte.

Comments (2) -

  • tabii buyuk koleksiyonlarda reflection'dan doğacak performans kaybını ve uygulamanın host edildiği ortamlardaki güvenlik kısıtlamaları dolayasıyla reflection'ı kullanamayabileceğimizi göz önünde bulundurmamızda yarar var
    • Kesinlikle haklısın GT . Reflection demek extra cost demek ancak bu cost .Net için seninde söylediğin daha büyük veri yapılarında bir loop oluşturacak şekilde kullanılmasının önüne geçilmesi gerekir. Ama örneğimiz üzerinden ele alacak olursak ignore edilebilir diyebiliriz.

      Değerli yorumun için teşekkürler Smile

Add comment