public void StartTracking(IChangeTrackableObject trackableObject, ObjectChangeTracker currentTracker = null)
        {
            Contract.Requires(() => trackableObject != null, "Given reference must be non-null to be able to track it");

            ChangeTrackingContext.ChangeTracker = currentTracker ?? new ObjectChangeTracker(ChangeTrackingContext.Configuration, TrackableObjectFactory, trackableObject);
            PropertyChanged += (sender, e) => TrackProperty(trackableObject, e.PropertyName);

            ITrackableType trackableType = ChangeTrackingContext.Configuration.GetTrackableType
                                           (
                trackableObject.GetActualTypeIfTrackable()
                                           );

            HashSet <PropertyInfo> trackableProperties;

            if (trackableType != null && trackableType.IncludedProperties.Count > 0)
            {
                trackableProperties = new HashSet <PropertyInfo>(trackableType.IncludedProperties);
            }
            else
            {
                trackableProperties = new HashSet <PropertyInfo>
                                      (
                    trackableObject.GetType().GetProperties(DefaultBindingFlags | BindingFlags.DeclaredOnly)
                    .Where(p => p.CanReadAndWrite())
                                      );
            }

            IEnumerable <Type> baseTypes = trackableType.Type.GetAllBaseTypes();

            if (baseTypes.Count() > 0)
            {
                foreach (Type baseType in baseTypes)
                {
                    if (ChangeTrackingContext.Configuration.CanTrackType(baseType))
                    {
                        foreach (PropertyInfo property in ChangeTrackingContext.Configuration.GetTrackableType(baseType).IncludedProperties)
                        {
                            if (property.GetCustomAttribute <DoNotTrackChangesAttribute>() == null)
                            {
                                trackableProperties.Add(property);
                            }
                        }
                    }
                }
            }

            foreach (PropertyInfo property in trackableProperties)
            {
                if (property.GetCustomAttribute <DoNotTrackChangesAttribute>() == null && !property.IsIndexer() && !PropertyInterceptor.ChangeTrackingMembers.Contains(property.Name))
                {
                    if (property.IsEnumerable())
                    {
                        ToTrackableCollection(property, trackableObject);
                        ChangeTrackingContext.CollectionProperties.Add(property);
                    }

                    TrackProperty(trackableObject, property.Name);
                }
            }
        }
        public IEnumerable <ITrackableType> GetAllTrackableBaseTypes(ITrackableType trackableType)
        {
            Contract.Requires(() => trackableType != null, "Given trackable type must be a non-null reference");

            lock (_syncLock)
                return(trackableType.Type.GetAllBaseTypes()
                       .Where(t => CanTrackType(t))
                       .Select(t => GetTrackableType(t)));
        }
        public void ConfiguresClassWithAllPropertiesExceptingNonTrackable()
        {
            IObjectChangeTrackingConfiguration config = ObjectChangeTracking.CreateConfiguration();

            config.TrackTypesFromAssembly(typeof(ConfigurationTest).GetTypeInfo().Assembly, searchSettings: new TypeSearchSettings
            {
                Mode   = TypeSearchMode.AttributeConfigurationOnly,
                Filter = t => t == typeof(TwoPropertiesOneTrackable)
            });

            ITrackableType trackableType = config.TrackableTypes.Single();

            Assert.AreEqual(1, trackableType.IncludedProperties.Count);
            Assert.AreEqual("Text", trackableType.IncludedProperties.Single().Name);
        }
        public void ConfiguresClassWithAllProperties()
        {
            IObjectChangeTrackingConfiguration config = ObjectChangeTracking.CreateConfiguration();

            config.TrackTypesFromAssembly(typeof(ConfigurationTest).GetTypeInfo().Assembly, searchSettings: new TypeSearchSettings
            {
                Mode   = TypeSearchMode.AttributeConfigurationOnly,
                Filter = t => t == typeof(AllProperties)
            });

            ITrackableType trackableType = config.TrackableTypes.Single();

            Assert.AreEqual(2, trackableType.IncludedProperties.Count);
            Assert.IsTrue(trackableType.IncludedProperties.Any(p => p.Name == "Text"));
            Assert.IsTrue(trackableType.IncludedProperties.Any(p => p.Name == "Text2"));
        }
        /// <summary>
        /// Determines if a given property holds an object type configured as a trackable type
        /// </summary>
        /// <param name="property">The whole property to check</param>
        /// <returns><literal>true</literal> if helds an object type configured as a trackable type, <literal>false</literal> if not </returns>
        public bool CanTrackProperty(PropertyInfo property)
        {
            Contract.Requires(() => property != null, "Property to check cannot be null");
            Contract.Requires(() => CanTrackType(property.DeclaringType.GetActualTypeIfTrackable()), "Declaring type must be configured as trackable");

            lock (_syncLock)
            {
                Contract.Assert(() => CanTrackType(property.GetBaseProperty().DeclaringType), "Declaring type must be configured as trackable even if it's a base class");

                ITrackableType trackableType = GetTrackableType(property.GetBaseProperty().DeclaringType);

                return(trackableType.IncludedProperties.Count == 0 ||
                       trackableType.IncludedProperties.Contains(property.GetBaseProperty()) ||
                       trackableType.IncludedProperties.Any(p => p.DeclaringType.IsAssignableFrom(property.DeclaringType) && p.Name == property.Name));
            }
        }
Beispiel #6
0
        public bool ShouldInterceptMethod(Type type, MethodInfo methodInfo)
        {
            bool isDynamicObject = type.IsDynamicObject();

            if ((!isDynamicObject && (!methodInfo.MethodIsPropertyAccessorOfReadWriteProperty() || !methodInfo.IsPropertyGetterOrSetter())) || _skippedMethods.Contains(methodInfo))
            {
                return(false);
            }

            ITrackableType trackableType = Configuration.TrackableTypes.SingleOrDefault(t => t.Type == type);

            if (trackableType == null)
            {
                return(false);
            }

            if (isDynamicObject && methodInfo.IsMethodOfDynamicObject())
            {
                return(_dynamicObjectGetterSetterMethods.Contains(methodInfo.GetRuntimeBaseDefinition()));
            }

            if (trackableType.IncludedProperties.Count == 0)
            {
                return(true);
            }

            return(trackableType.IncludedProperties
                   .Concat
                   (
                       Configuration.GetAllTrackableBaseTypes(trackableType)
                       .SelectMany(t => t.IncludedProperties)
                   ).Any(p =>
            {
                var accessorMethods = new[] { p.GetMethod.GetBaseDefinition(), p.SetMethod.GetBaseDefinition() };

                return accessorMethods.Contains(methodInfo.GetBaseDefinition()) ||
                accessorMethods.Any(m => m.DeclaringType.IsAssignableFrom(methodInfo.GetBaseDefinition().DeclaringType) && m.Name == methodInfo.Name);
            }));
        }
        public bool ImplementsBaseType(Type someType, out ITrackableType baseType)
        {
            baseType = TrackableInterfaceTypes.SingleOrDefault(t => t.Type.IsAssignableFrom(someType.GetActualTypeIfTrackable()));

            return(baseType != null);
        }
        public object Create(object some = null, Type typeToTrack = null, ObjectChangeTracker reusedTracker = null, object parentObject = null, PropertyInfo propertyToSet = null, object[] constructorArguments = null)
        {
            typeToTrack = typeToTrack ?? some.GetType();

            ITrackableType interfaceTrackableType = null;

            if ((Configuration.CanTrackType(typeToTrack) || Configuration.ImplementsBaseType(typeToTrack, out interfaceTrackableType)) && !typeToTrack.IsTrackable())
            {
                if (interfaceTrackableType != null)
                {
                    Configuration.TrackThisTypeRecursive
                    (
                        typeToTrack,
                        trackableType =>
                    {
                        if (trackableType.Type == typeToTrack)
                        {
                            trackableType.IncludeProperties(interfaceTrackableType.IncludedProperties.ToArray());
                        }
                    }
                    );
                }

                Contract.Assert(typeToTrack.IsClass && !typeToTrack.IsAbstract && !typeToTrack.IsSealed, $"The object type to track '{typeToTrack.AssemblyQualifiedName}' must be a non-abstract, non-sealed class");

                ProxyGenerationOptions options = new ProxyGenerationOptions(new SimplePropertyInterceptionHook(Configuration));
                options.AddMixinInstance(new ChangeTrackableObjectMixin(Configuration, this));
                options.AdditionalAttributes.Add(AttributeUtil.CreateBuilder(typeof(DebuggerDisplayAttribute), new[] { $"{typeToTrack.FullName}Proxy" }));

                List <IInterceptor> interceptors = new List <IInterceptor>
                {
                    new SimplePropertyInterceptor()
                };

                if (typeToTrack.IsDynamicObject())
                {
                    interceptors.Add(new DynamicObjectInterceptor(Configuration, this));
                }

                object proxy;

                if (some != null)
                {
                    proxy = ProxyGenerator.CreateClassProxyWithTarget
                            (
                        classToProxy: typeToTrack,
                        additionalInterfacesToProxy: new[] { typeof(IChangeTrackableObject) },
                        target: some,
                        options: options,
                        interceptors: interceptors.ToArray()
                            );
                }
                else
                {
                    proxy = ProxyGenerator.CreateClassProxy
                            (
                        classToProxy: typeToTrack,
                        additionalInterfacesToProxy: new[] { typeof(IChangeTrackableObject) },
                        options: options,
                        constructorArguments: constructorArguments,
                        interceptors: interceptors.ToArray()
                            );
                }


                IChangeTrackableObject      trackableObject = (IChangeTrackableObject)proxy;
                ObjectChangeTrackingContext trackingContext = trackableObject.GetChangeTrackingContext();

                trackingContext.State = ChangeTrackableObjectState.Constructing;
                trackableObject.StartTracking(trackableObject, reusedTracker);
                trackingContext.State = ChangeTrackableObjectState.Ready;

                HashSet <PropertyInfo> propertiesToTrack =
                    new HashSet <PropertyInfo>(Configuration.GetTrackableType(typeToTrack).IncludedProperties);

                if (propertiesToTrack.Count() == 0)
                {
                    foreach (PropertyInfo property in typeToTrack.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                    {
                        propertiesToTrack.Add(property);
                    }
                }

                foreach (PropertyInfo property in propertiesToTrack)
                {
                    if (!property.IsIndexer() && property.CanReadAndWrite())
                    {
                        object propertyValue = property.GetValue(trackableObject);

                        if (propertyValue != null)
                        {
                            Create(propertyValue, trackableObject.GetChangeTrackingContext().ChangeTracker, proxy, property);
                        }
                        else
                        {
                            Create(property.PropertyType, trackableObject.GetChangeTrackingContext().ChangeTracker, proxy, property);
                        }
                    }
                }

                if (propertyToSet != null)
                {
                    propertyToSet.SetValue(parentObject ?? proxy, proxy);
                }

                trackableObject.AcceptChanges();

                return(proxy);
            }
            else
            {
                return(some);
            }
        }