Esempio n. 1
0
        public void When_Clear_Collection_Should_Work()
        {
            IList <Order> orders    = Helper.GetOrdersIList();
            IList <Order> trackable = orders.AsTrackable();

            trackable.Clear();

            IChangeTrackableCollection <Order> trackableCollection = trackable.CastToIChangeTrackableCollection();

            trackableCollection.IsChanged.Should().BeTrue();
            trackableCollection.DeletedItems.Should().NotBeEmpty();
            trackableCollection.UnchangedItems.Should().BeEmpty();
        }
        /// <summary>
        /// Turns given object and all associates to untrackable objects (i.e. POCO objects).
        /// </summary>
        /// <param name="some">The whole object to untrack</param>
        /// <returns>The untracked version of given object</returns>
        internal static object ToUntypedUntracked(this object some)
        {
            if (some == null)
            {
                return(some);
            }

            IChangeTrackableObject trackable = some as IChangeTrackableObject;

            if (trackable != null)
            {
                IProxyTargetAccessor proxyTargetAccessor = (IProxyTargetAccessor)trackable;
                object target = proxyTargetAccessor.DynProxyGetTarget();

                object unwrapped = target.CloneIt(some.GetType().GetActualTypeIfTrackable());

                ObjectChangeTracker changeTracker = (ObjectChangeTracker)trackable.GetChangeTracker();

                foreach (KeyValuePair <string, ObjectPropertyChangeTracking> dynamicProperty in changeTracker.DynamicPropertyTrackings)
                {
                    object propertyValueToSet;
                    IChangeTrackableCollection trackableCollection = dynamicProperty.Value.CurrentValue as IChangeTrackableCollection;

                    if (trackableCollection != null)
                    {
                        propertyValueToSet = ((IEnumerable)trackableCollection).ToUntrackedEnumerable(trackableCollection.GetType());
                    }
                    else
                    {
                        propertyValueToSet = dynamicProperty.Value.CurrentValue.ToUntracked();
                    }

                    unwrapped.SetDynamicMember
                    (
                        dynamicProperty.Value.PropertyName,
                        propertyValueToSet
                    );
                }

                Contract.Assert(() => !(unwrapped is IProxyTargetAccessor), "To convert a tracked object to untracked one the whole tracked object must be created from a pre-existing object");

                return(unwrapped);
            }
            else
            {
                return(some);
            }
        }
        protected override void InterceptMethod(IInvocation invocation, IHasParent withParent)
        {
            Type collectionType = invocation.Method.DeclaringType;

            if (collectionType.IsEnumerable() && invocation.Arguments.Length == 1)
            {
                IChangeTrackableCollection trackableCollection = (IChangeTrackableCollection)withParent;
                IChangeTrackableObject     changedItem         =
                    invocation.Arguments[0] as IChangeTrackableObject
                    ?? TrackableObjectFactory.CreateFrom(invocation.Arguments[0]) as IChangeTrackableObject;

                IEnumerable <object> currentItems;
                Type collectionItemType   = withParent.GetCollectionItemType();
                bool itemsAreKeyValuePair = collectionItemType.GetTypeInfo().IsGenericType&& collectionItemType == typeof(KeyValuePair <,>).MakeGenericType(collectionItemType.GenericTypeArguments);

                if (itemsAreKeyValuePair)
                {
                    currentItems = ((IEnumerable)withParent).Cast <object>();
                }
                else
                {
                    currentItems = ((IEnumerable <object>)withParent).ToList();
                }

                invocation.Arguments[0] = changedItem ?? invocation.Arguments[0];
                invocation.Proceed();

                KeyValuePair <Type, CollectionImplementation> implementation =
                    Configuration.Collections.GetImplementation(collectionType);

                CollectionChangeContext changeContext = new CollectionChangeContext
                                                        (
                    !itemsAreKeyValuePair ? (IEnumerable <object>)trackableCollection : Enumerable.Empty <object>(),
                    currentItems,
                    trackableCollection.ParentObjectProperty,
                    trackableCollection.AddedItems,
                    trackableCollection.RemovedItems
                                                        );

                if (!itemsAreKeyValuePair)
                {
                    Activator.CreateInstance(implementation.Value.ChangeInterceptor.MakeGenericType(trackableCollection.GetCollectionItemType()), changeContext)
                    .CallMethod(invocation.Method.Name, invocation.Arguments);
                }

                switch (changeContext.Change)
                {
                case CollectionChange.Add:
                    trackableCollection.RaiseCollectionChanged(NotifyCollectionChangedAction.Add, new[] { changedItem });
                    break;

                case CollectionChange.Remove:
                    trackableCollection.RaiseCollectionChanged(NotifyCollectionChangedAction.Remove, new[] { changedItem });
                    break;
                }
            }
            else
            {
                invocation.Proceed();
            }
        }
        public object CreateForCollection(object some, IChangeTrackableObject parentObject, PropertyInfo parentObjectProperty)
        {
            if (some.IsTrackable())
            {
                return(some);
            }

            Type collectionType = Configuration.Collections
                                  .GetImplementation(parentObjectProperty.PropertyType).Key;

            Type collectionImplementation;

            if (parentObjectProperty.PropertyType.IsGenericType && parentObjectProperty.PropertyType == collectionType.MakeGenericType(parentObjectProperty.PropertyType.GenericTypeArguments))
            {
                collectionImplementation = collectionType.MakeGenericType(parentObjectProperty.PropertyType.GenericTypeArguments);
            }
            else
            {
                collectionImplementation = parentObjectProperty.PropertyType.GetInterfaces().SingleOrDefault
                                           (
                    i =>
                {
                    return(i.IsGenericType && i == collectionType.MakeGenericType(i.GenericTypeArguments[0]));
                }
                                           );
            }

            Contract.Assert(collectionImplementation != null);

            if (some == null)
            {
                some = Configuration.Collections
                       .GetImplementation(parentObjectProperty.PropertyType).Value.Type
                       .CreateInstanceWithGenericArgs(null, collectionImplementation.GenericTypeArguments[0]);
            }

            Contract.Assert(some != null, "Either if a collection object is provided or not, a proxied instance of the whole collection type must be created");

            Type genericCollectionType  = some.GetType().GetGenericArguments().Last();
            bool canTrackCollectionType = Configuration.CanTrackType(genericCollectionType);

            ProxyGenerationOptions         options             = new ProxyGenerationOptions(new CollectionterceptionHook());
            ChangeTrackableCollectionMixin changeTrackingMixin = new ChangeTrackableCollectionMixin();

            changeTrackingMixin.GetChangeTrackingContext().Configuration = Configuration;
            options.AddMixinInstance(changeTrackingMixin);

            KeyValuePair <Type, CollectionImplementation> collectionImplementationDetail
                = Configuration.Collections.GetImplementation(parentObjectProperty.PropertyType);

            Contract.Assert(collectionImplementationDetail.Key.MakeGenericType(collectionImplementation.GenericTypeArguments).IsAssignableFrom(parentObjectProperty.PropertyType), $"Trackable collection implementation of type '{collectionImplementationDetail.Key.AssemblyQualifiedName}' cannot be set to the target property '{parentObjectProperty.DeclaringType.FullName}.{parentObjectProperty.Name}' with type '{parentObjectProperty.PropertyType.AssemblyQualifiedName}'. This isn't supported because it might require a downcast. Please provide a collection change tracking configuration to work with the more concrete interface.");

            object targetList;

            if (!canTrackCollectionType)
            {
                targetList = some;
            }
            else
            {
                targetList = collectionImplementationDetail.Value.Type.CreateInstanceWithGenericArgs
                             (
                    new[]
                {
                    typeof(EnumerableExtensions).GetMethod("MakeAllTrackable")
                    .MakeGenericMethod(genericCollectionType)
                    .Invoke(null, new[] { some, Configuration, this, parentObjectProperty, parentObject })
                },
                    genericCollectionType
                             );
            }

            Contract.Assert(targetList != null, "List to proxy is mandatory");

            IChangeTrackableCollection proxy = (IChangeTrackableCollection)ProxyGenerator.CreateInterfaceProxyWithTarget
                                               (
                collectionImplementationDetail.Key.MakeGenericType(some.GetType().GetGenericArguments()),
                new[] { typeof(IChangeTrackableCollection), typeof(IReadOnlyChangeTrackableCollection) },
                targetList,
                options,
                new CollectionPropertyInterceptor(Configuration, this)
                                               );

            proxy.ParentObject         = parentObject;
            proxy.ParentObjectProperty = parentObjectProperty;

            proxy.CollectionChanged += (sender, e) =>
                                       parentObject.RaisePropertyChanged(parentObject, parentObjectProperty.Name);

            return(proxy);
        }