예제 #1
0
        public static IChangeCollection <T> GetChanges <T>(T oldObject, T newObject, Expression <Func <T, object> > expression = null)
        {
            var a = GetValueOfExpressionFor(oldObject, expression);
            var b = GetValueOfExpressionFor(newObject, expression);

            var propertyPath = PropertyPathHelper.GetPropertyPath(expression);

            using (var context = new ContextPair(a, b))
                return(GetRecursiveChanges <ChangeCollection <T> >(context, propertyPath));
        }
예제 #2
0
        private static TCollection GetRecursiveChanges <TCollection>(ContextPair contextPair, string basePropertyPath) where TCollection : ChangeCollection, new()
        {
            var oldInstance = contextPair.OldInstanceContext.Instance;
            var newInstance = contextPair.NewInstanceContext.Instance;

            var type = GetTypeFromObjects(oldInstance, newInstance);

            if (type == null)
            {
                return(new TCollection());
            }

            if (IsSimpleType(type))
            {
                var change = GetShallowChange(basePropertyPath, oldInstance, newInstance);
                if (change == Change.Empty)
                {
                    return(new TCollection());
                }

                var collection = new TCollection();
                collection.Add(change);

                return(collection);
            }

            var result = new TCollection();

            ScanForChanges(contextPair, new ObjectPath()
            {
                OldInstance      = oldInstance,
                NewInstance      = newInstance,
                BasePropertyPath = null
            }, result);

            var objectPathQueue = contextPair.ObjectPathQueue;

            while (objectPathQueue.Count > 0)
            {
                var objectPath = objectPathQueue.Dequeue();

                var objectPathType = GetTypeFromObjects(
                    objectPath.OldInstance,
                    objectPath.NewInstance);
                if (objectPathType == null)
                {
                    continue;
                }

                ScanForChanges(contextPair, objectPath, result);
            }

            return(result);
        }
예제 #3
0
 public static IChangeCollection GetChanges(object oldObject, object newObject)
 {
     using (var context = new ContextPair(oldObject, newObject))
         return(GetRecursiveChanges <ChangeCollection>(context, null));
 }
예제 #4
0
        private static void EnqueueObjectPathQueues(
            ContextPair contextPair,
            ObjectPath objectPath)
        {
            var oldInstance = objectPath.OldInstance;
            var newInstance = objectPath.NewInstance;

            var type = GetTypeFromObjects(oldInstance, newInstance);

            if (type == null)
            {
                throw new InvalidOperationException("The type could not be determined.");
            }

            if (IsSimpleType(type))
            {
                throw new InvalidOperationException("This method can't be called with simple types.");
            }

            var seenObjectsOld = contextPair.OldInstanceContext.SeenObjects;
            var seenObjectsNew = contextPair.NewInstanceContext.SeenObjects;

            if (seenObjectsOld.Contains(objectPath.OldInstance) || seenObjectsNew.Contains(objectPath.NewInstance))
            {
                return;
            }

            if (objectPath.OldInstance != null)
            {
                seenObjectsOld.Add(objectPath.OldInstance);
            }

            if (objectPath.NewInstance != null)
            {
                seenObjectsNew.Add(objectPath.NewInstance);
            }

            var objectPathQueue = contextPair.ObjectPathQueue;

            var properties = type.GetProperties();

            foreach (var property in properties)
            {
                try
                {
                    objectPathQueue.Enqueue(new ObjectPath()
                    {
                        OldInstance      = oldInstance == null ? null : property.GetValue(oldInstance),
                        NewInstance      = newInstance == null ? null : property.GetValue(newInstance),
                        Properties       = property.PropertyType.GetProperties(),
                        BasePropertyPath = AddToPropertyPath(
                            objectPath.BasePropertyPath,
                            property.Name)
                    });
                }
                catch (Exception ex)
                {
                    throw new Exception($"An error occured while comparing {AddToPropertyPath(objectPath.BasePropertyPath, property.Name)}.", ex);
                }
            }
        }
예제 #5
0
        private static void ScanForChanges(ContextPair contextPair, ObjectPath objectPath, ChangeCollection result)
        {
            var pathType = GetTypeFromObjects(
                objectPath.OldInstance,
                objectPath.NewInstance);

            if (IsSimpleType(pathType))
            {
                var shallowChange = GetShallowChange(
                    objectPath.BasePropertyPath,
                    objectPath.OldInstance,
                    objectPath.NewInstance);
                if (shallowChange != Change.Empty)
                {
                    result.Add(shallowChange);
                    if (objectPath.ContainerChange != Change.Empty)
                    {
                        result.Add(objectPath.ContainerChange);
                    }
                }
            }
            else if (IsEnumerableType(pathType))
            {
                Array itemsOldArray;
                Array itemsNewArray;

                if (pathType.IsArray)
                {
                    itemsOldArray = (Array)objectPath.OldInstance;
                    itemsNewArray = (Array)objectPath.NewInstance;
                }
                else
                {
                    var genericType = pathType
                                      .GetInterfaces()
                                      .Single(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable <>))
                                      .GetGenericArguments()
                                      .Single();

                    var toArrayMethod = typeof(Enumerable)
                                        .GetMethods()
                                        .Single(x =>
                                                x.Name == nameof(Enumerable.ToArray) &&
                                                x.IsGenericMethod)
                                        .MakeGenericMethod(genericType);

                    var emptyMethod = typeof(Array)
                                      .GetMethods()
                                      .Single(x =>
                                              x.Name == nameof(Array.Empty) &&
                                              x.IsGenericMethod)
                                      .MakeGenericMethod(genericType);

                    itemsOldArray = objectPath.OldInstance == null
                        ? (Array)emptyMethod.Invoke(null, Array.Empty <object>())
                        : (Array)toArrayMethod.Invoke(null, new[] { objectPath.OldInstance });

                    itemsNewArray = objectPath.NewInstance == null
                        ? (Array)emptyMethod.Invoke(null, Array.Empty <object>())
                        : (Array)toArrayMethod.Invoke(null, new[] { objectPath.NewInstance });
                }

                var maxCount = Math.Max(itemsOldArray.Length, itemsNewArray.Length);

                var newItemsOldArray = new object[maxCount];
                var newItemsNewArray = new object[maxCount];

                Array.Copy(itemsOldArray, newItemsOldArray, itemsOldArray.Length);
                Array.Copy(itemsNewArray, newItemsNewArray, itemsNewArray.Length);

                var shallowChange = GetShallowChange(
                    objectPath.BasePropertyPath ?? string.Empty,
                    objectPath.OldInstance,
                    objectPath.NewInstance);

                for (var i = 0; i < maxCount; i++)
                {
                    var itemOldInstance = newItemsOldArray[i];
                    var itemNewInstance = newItemsNewArray[i];

                    var itemType = GetTypeFromObjects(itemOldInstance, itemNewInstance);
                    if (itemType == null)
                    {
                        continue;
                    }

                    var arrayItemObjectPath = new ObjectPath()
                    {
                        BasePropertyPath = AddToPropertyPath(
                            objectPath.BasePropertyPath,
                            i.ToString()),
                        OldInstance     = itemOldInstance,
                        NewInstance     = itemNewInstance,
                        Properties      = itemType.GetProperties(),
                        ContainerChange = shallowChange
                    };

                    contextPair.ObjectPathQueue.Enqueue(arrayItemObjectPath);
                }
            }
            else
            {
                EnqueueObjectPathQueues(
                    contextPair,
                    objectPath);
            }
        }
        private static void ScanForChanges(ContextPair contextPair, ObjectPath objectPath, ChangeCollection result)
        {
            var pathType = GetTypeFromObjects(
                objectPath.OldInstance,
                objectPath.NewInstance);

            if (IsSimpleType(pathType))
            {
                var shallowChange = GetShallowChange(
                    objectPath.BasePropertyPath,
                    objectPath.OldInstance,
                    objectPath.NewInstance);
                if (shallowChange == Change.Empty)
                {
                    return;
                }

                result.Add(shallowChange);
                if (objectPath.ParentChanges == null)
                {
                    return;
                }

                foreach (var parentChange in objectPath.ParentChanges)
                {
                    result.Add(parentChange);
                }
            }
            else if (IsEnumerableType(pathType))
            {
                if (pathType == null)
                {
                    throw new InvalidOperationException("Type was enumerable despite being null.");
                }

                Array?itemsOldArray;
                Array?itemsNewArray;

                if (pathType.IsArray)
                {
                    itemsOldArray = (Array?)objectPath.OldInstance;
                    itemsNewArray = (Array?)objectPath.NewInstance;
                }
                else
                {
                    var genericType = pathType
                                      .GetInterfaces()
                                      .Single(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable <>))
                                      .GetGenericArguments()
                                      .Single();

                    var toArrayMethod = typeof(Enumerable)
                                        .GetMethods()
                                        .Single(x =>
                                                x.Name == nameof(Enumerable.ToArray) &&
                                                x.IsGenericMethod)
                                        .MakeGenericMethod(genericType);

                    var emptyMethod = typeof(Array)
                                      .GetMethods()
                                      .Single(x =>
                                              x.Name == nameof(Array.Empty) &&
                                              x.IsGenericMethod)
                                      .MakeGenericMethod(genericType);

                    itemsOldArray = objectPath.OldInstance == null
                        ? (Array)emptyMethod.Invoke(null, Array.Empty <object>())
                        : (Array)toArrayMethod.Invoke(null, new[] { objectPath.OldInstance });

                    itemsNewArray = objectPath.NewInstance == null
                        ? (Array)emptyMethod.Invoke(null, Array.Empty <object>())
                        : (Array)toArrayMethod.Invoke(null, new[] { objectPath.NewInstance });
                }

                var maxCount = Math.Max(
                    itemsOldArray?.Length ?? 0,
                    itemsNewArray?.Length ?? 0);

                var newItemsOldArray = new object[maxCount];
                var newItemsNewArray = new object[maxCount];

                Array.Copy(itemsOldArray ?? Array.Empty <object>(), newItemsOldArray, itemsOldArray?.Length ?? 0);
                Array.Copy(itemsNewArray ?? Array.Empty <object>(), newItemsNewArray, itemsNewArray?.Length ?? 0);

                var shallowChange = new Change(
                    objectPath.BasePropertyPath ?? string.Empty,
                    objectPath.OldInstance,
                    objectPath.NewInstance);
                var newParentChanges = new[]
                {
                    shallowChange
                };

                var parentChanges = objectPath.ParentChanges == null ?
                                    newParentChanges :
                                    objectPath
                                    .ParentChanges
                                    .Union(newParentChanges)
                                    .Distinct()
                                    .ToArray();

                for (var i = 0; i < maxCount; i++)
                {
                    var itemOldInstance = newItemsOldArray[i];
                    var itemNewInstance = newItemsNewArray[i];

                    var itemType = GetTypeFromObjects(itemOldInstance, itemNewInstance);
                    if (itemType == null)
                    {
                        continue;
                    }

                    var arrayItemObjectPath = new ObjectPath()
                    {
                        BasePropertyPath = AddToPropertyPath(
                            objectPath.BasePropertyPath,
                            i.ToString()),
                        OldInstance   = itemOldInstance,
                        NewInstance   = itemNewInstance,
                        Properties    = itemType.GetProperties(),
                        ParentChanges = parentChanges
                    };

                    contextPair.ObjectPathQueue.Enqueue(arrayItemObjectPath);
                }
            }
            else
            {
                EnqueueObjectPathQueues(
                    contextPair,
                    objectPath);
            }
        }