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)); } }
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); } }