private void CDCforComplexTypeCollection(ICollection oldEntry, ICollection newEntry, ChangeDataCaptureList CDCs, Dictionary <String, String> fieldList, List <string> keyFields, string parentName, string mainKey, string classTitle, DateTime dateChanged) { //do not comare more than 10 entries // if ((oldEntry.Count > 10) || (newEntry.Count > 10)) // return; //loop through oldEntry collection to find out updates and deletions foreach (var itemO in oldEntry) { if (itemO == null) { continue; } //extract key value //var oldProperties = itemO.GetType().GetProperties(); //var oldKey = oldProperties.Where(x => Attribute.IsDefined(x, typeof(CDCPrimaryKeyAttribute)) || (x.Name == keyField)).FirstOrDefault()?.GetValue(itemO)?.ToString(); var oldKey = GetKeyFieldsValue(itemO, keyFields); //key value is necessary for compare process, so compare process should be terminated if there is no key defiend for objects in collection if (string.IsNullOrWhiteSpace(oldKey)) { return; } object newData = null; //loop through newEntry items to find the item with the same key of item from oldEntry foreach (var itemN in newEntry) { if (itemN == null) { continue; } //extract key value //var newProperties = itemN.GetType().GetProperties(); //var newKey = newProperties.Where(x => Attribute.IsDefined(x, typeof(CDCPrimaryKeyAttribute)) || (x.Name == keyField)).FirstOrDefault()?.GetValue(itemN)?.ToString(); var newKey = GetKeyFieldsValue(itemN, keyFields); //key value is necessary for compare process, so compare process should be terminated if there is no key defiend for objects in collection if (string.IsNullOrWhiteSpace(newKey)) { return; } //if the item exists in newEntry collection if (oldKey == newKey) { newData = itemN; break; } } //compare items for changes ChangeDataCaptureList tempCDC = GetChanges(itemO, newData, dateChanged, fieldList, keyFields, parentName, mainKey, classTitle); if (tempCDC != null) { CDCs.AddRange(tempCDC); } } //loop through newEntry collection to find out new Assignments foreach (var itemN in newEntry) { if (itemN == null) { continue; } //var newProperties = itemN.GetType().GetProperties(); //var newKey = newProperties.Where(x => Attribute.IsDefined(x, typeof(CDCPrimaryKeyAttribute)) || (x.Name == keyField)).FirstOrDefault()?.GetValue(itemN)?.ToString(); var newKey = GetKeyFieldsValue(itemN, keyFields); if (string.IsNullOrWhiteSpace(newKey)) { return; } object oldData = null; bool found = false; foreach (var itemO in oldEntry) { if (itemO == null) { continue; } //var oldProperties = itemO.GetType().GetProperties(); //var oldKey = oldProperties.Where(x => Attribute.IsDefined(x, typeof(CDCPrimaryKeyAttribute)) || (x.Name == keyField)).FirstOrDefault()?.GetValue(itemO)?.ToString(); var oldKey = GetKeyFieldsValue(itemO, keyFields); if (string.IsNullOrWhiteSpace(oldKey)) { return; } if (oldKey == newKey) { found = true; break; } } //if item not found in old list so this will be assumed as a new assignment if (!found) { ChangeDataCaptureList tempCDC = GetChanges(oldData, itemN, dateChanged, fieldList, keyFields, parentName, mainKey, classTitle); if (tempCDC != null) { CDCs.AddRange(tempCDC); } } } }
/// <summary> /// This method is will compare two objects and returns the Change Data Capture List as result /// </summary> /// <param name="oldEntry">data model object which contains previous state of data (Could be the DAL object)</param> /// <param name="newEntry">data model object which contains new state of data (Could be BLL object)</param> /// <param name="dateChanged">Data time of change</param> /// <param name="fieldList">Optional fieldList to compare, also fields with CDCFieldAttribute annotation in will be compare</param> /// <param name="keyField">Optional key field name, also field with CDCPrimaryKeyAttribute annotation will be used as key field</param> /// <param name="parentName">Optional Name of parent or owner class</param> /// <param name="mainKey">Optional Key field value of parents or owner objects</param> /// <param name="classTitle">Optional preferted title for current object, also class annotation CDCClassTitleAttribute will be used to determain class title</param> /// <returns></returns> public ChangeDataCaptureList GetChanges(object oldEntry, object newEntry, DateTime dateChanged, Dictionary <String, String> fieldList = null, List <string> keyFields = null, string parentName = "", string mainKey = "", string classTitle = "") { ChangeDataCaptureList CDCs = new ChangeDataCaptureList(); //there is nothing to compare if ((oldEntry == null) && (newEntry == null)) { return(CDCs); } //new assigment happend and all properties of newEntry should be captured if (oldEntry == null) { CaptureNewAssignment(newEntry, CDCs, fieldList, keyFields, parentName, mainKey, classTitle, dateChanged); return(CDCs); } //deletion happend and all properties of oldEntry should be captured if (newEntry == null) { CaptureNewDeletion(oldEntry, CDCs, fieldList, keyFields, parentName, mainKey, classTitle, dateChanged); return(CDCs); } if (IsCollection(oldEntry) || IsCollection(oldEntry)) { CDCforCollection((System.Collections.ICollection)oldEntry, (System.Collections.ICollection)newEntry, CDCs, fieldList, keyFields, "", parentName, mainKey, classTitle, dateChanged); return(CDCs); } var oldType = oldEntry.GetType(); var newType = newEntry.GetType(); //if(oldType != newType) //{ // return CDCs; //Types don't match, cannot capture changes //} //DAL var oldProperties = oldType.GetProperties(); //var newProperties = newType.GetProperties(); //BLL var newProperties = newType.GetProperties().Where(pr => pr.GetCustomAttributes(typeof(CDCFieldAttribute), false).Any() || ((fieldList != null) && fieldList.ContainsKey(pr.Name))); //extract key value //var key = newType.GetProperties().Where(x => Attribute.IsDefined(x, typeof(CDCPrimaryKeyAttribute)) || (x.Name == keyField)).FirstOrDefault()?.GetValue(newEntry)?.ToString(); var key = GetKeyFieldsValue(newEntry, keyFields); var primaryKey = (string.IsNullOrWhiteSpace(mainKey) ? string.Empty : mainKey) + (string.IsNullOrWhiteSpace(mainKey) || string.IsNullOrWhiteSpace(key) ? string.Empty : ",") + (string.IsNullOrWhiteSpace(key) ? string.Empty : key); //var className = (string.IsNullOrWhiteSpace(parentName) ? string.Empty : parentName + ".") + oldEntry.GetType().Name; string _classTitle = string.IsNullOrWhiteSpace(classTitle) ? (newEntry.GetType().GetCustomAttribute(typeof(CDCClassTitleAttribute)) as CDCClassTitleAttribute)?.Title : classTitle; var className = string.IsNullOrWhiteSpace(parentName) ? (string.IsNullOrWhiteSpace(_classTitle) ? newEntry.GetType().Name : _classTitle) : parentName; //loop all old properties (DAL object) foreach (var oldProperty in oldProperties) { //find maching property from newEntry (BLL object) var matchingProperty = newProperties.Where(x => ((Attribute.IsDefined(x, typeof(CDCFieldAttribute)) && (x.GetCustomAttributes(typeof(CDCFieldAttribute), false)[0] as CDCFieldAttribute).FieldName == oldProperty.Name) || ((fieldList != null) && fieldList.ContainsKey(oldProperty.Name)) )) //&& x.PropertyType == oldProperty.PropertyType) .FirstOrDefault(); //no match find so continue if (matchingProperty == null) { continue; } //if (matchingProperty.PropertyType.IsGenericType) //{ // continue; //} //if (typeof(System.Collections.IEnumerable).IsAssignableFrom(matchingProperty.PropertyType)) //{ // // ... //} //if the property is Collection if (IsCollection(matchingProperty)) { CDCforCollection((System.Collections.ICollection)oldProperty.GetValue(oldEntry, null), (System.Collections.ICollection)matchingProperty.GetValue(newEntry, null), CDCs, fieldList, keyFields, oldProperty.Name, className, primaryKey, classTitle, dateChanged); continue; } //if the property is a complex object so go for recursive process if ((!IsEnum(matchingProperty.PropertyType)) && (!IsSimple(matchingProperty.PropertyType))) { ChangeDataCaptureList nestedCDCs = GetChanges(oldProperty.GetValue(oldEntry), matchingProperty.GetValue(newEntry), dateChanged, fieldList, keyFields, className + "." + matchingProperty.Name, primaryKey, ""); CDCs.AddRange(nestedCDCs); continue; } //extract old and new values string oldValue = string.Empty; string newValue = string.Empty; if (IsEnum(matchingProperty.PropertyType)) { getEnumValues(oldEntry, newEntry, oldProperty, matchingProperty, out oldValue, out newValue); } else { oldValue = oldProperty.GetValue(oldEntry)?.ToString(); newValue = matchingProperty.GetValue(newEntry)?.ToString(); } string mask = (matchingProperty.GetCustomAttribute(typeof(CDCMaskValueAttribute)) as CDCMaskValueAttribute)?.Mask; string propName = Attribute.IsDefined(matchingProperty, typeof(CDCFieldAttribute)) ? (matchingProperty.GetCustomAttribute(typeof(CDCFieldAttribute)) as CDCFieldAttribute)?.Title : ((fieldList != null) && fieldList.ContainsKey(matchingProperty.Name)) ? fieldList[matchingProperty.Name] : matchingProperty.Name; //compare old and new values and capture as a new change if there is difference if (oldValue != newValue) { CDCs.Add(new ChangeDataCapture(mask) { ChangeType = ChangeType.Update, PrimaryKey = primaryKey, DateChanged = dateChanged, ClassName = className, PropertyName = propName, OldValue = oldValue, NewValue = newValue, OldEntry = oldEntry, NewEntry = newEntry }); } } return(CDCs); }
private void CaptureAllCollection(System.Collections.ICollection entry, ChangeType changeType, string mask, ChangeDataCaptureList CDCs, string parentName, string mainKey, string propertyName, DateTime dateChanged) { bool?containsSimpleType = null; //Test the item types of Enumerable oldEntry foreach (var item in entry) { containsSimpleType = IsEnum(item.GetType()) || IsSimple(item.GetType()); break; } if (containsSimpleType == null) { return; } object oldEntry = null; object newEntry = null; switch (changeType) { case ChangeType.Assign: newEntry = entry; break; case ChangeType.Remove: oldEntry = entry; break; } if (containsSimpleType == true) { foreach (var item in entry) { CDCs.Add(new ChangeDataCapture(mask) { ChangeType = changeType, PrimaryKey = mainKey, DateChanged = dateChanged, ClassName = parentName, PropertyName = propertyName, OldValue = item?.ToString(), NewValue = string.Empty, OldEntry = oldEntry, NewEntry = newEntry }); } } else if (containsSimpleType == false) { foreach (var item in entry) { if (item == null) { continue; } object oldV = null; object newV = null; switch (changeType) { case ChangeType.Assign: newV = item; break; case ChangeType.Remove: oldV = item; break; } ChangeDataCaptureList tempCDC = GetChanges(oldV, newV, dateChanged, null, null, parentName, mainKey); if (tempCDC != null) { CDCs.AddRange(tempCDC); } } } }