/// <summary> /// Mutates an object instance against the current context and the specified <see cref="MutationAttribute" />s. /// </summary> /// <typeparam name="T">The type to consult during mutation.</typeparam> /// <param name="context">Describes the type of object being mutated and provides services and context for mutation.</param> /// <param name="attributes">The list of <see cref="MutationAttribute" />s to modify the specified <paramref name="instance" /> against.</param> /// <param name="inferValue">If <c>true</c>, the <see cref="MutationContext{T}.ObjectInstance" /> will be mutated in place of specified <paramref name="instance" />.</param> /// <param name="instance">The instance to be modified.</param> /// <returns>The object whose value and properties has been modified according to any associated <see cref="MutationAttribute" />s and <see cref="IMutableObject" /> implementation.</returns> /// <exception cref="ArgumentNullException">When <paramref name="context" /> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">When <paramref name="attributes" /> is <c>null</c>.</exception> private static T GetMutatedObject <T>(MutationContext <T> context, IEnumerable <MutationAttribute> attributes, bool inferValue, T instance = default(T)) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (attributes == null) { throw new ArgumentNullException(nameof(attributes)); } instance = inferValue ? context.ObjectInstance : instance; // Step 1: Mutate the object properties' mutation attributes if (instance != null) { var properties = instance.GetType().GetRuntimeProperties().Where(p => AttributeStore.IsPublic(p) && !p.GetIndexParameters().Any()); foreach (var property in properties) { GetMutatedProperty <T, object>(context, property, true); } } // Step 2: Mutate the object's mutation attributes instance = GetMutatedValue(context, attributes, instance); // Step 3: Test for IMutableObject implementation (instance as IMutableObject)?.Mutate(context); return(instance); }
/// <summary> /// Mutates the specified value against the specified property of the instance associated with the current context. /// </summary> /// <typeparam name="T">The type to consult during mutation.</typeparam> /// <typeparam name="P">The property type to consult for <see cref="MutationAttribute" />s.</typeparam> /// <param name="context">Describes the type of object being mutated and provides services and context for mutation.</param> /// <param name="property">The property info that describes the value to be modified.</param> /// <param name="inferValue">If <c>true</c>, the current <paramref name="property" /> value will be mutated in place of specified <paramref name="value" />.</param> /// <param name="value">The value to be mutated when <paramref name="inferValue" /> is <c>false</c>.</param> /// <returns>The value that has been modified according to any <see cref="MutationAttribute" />s associated with the specified property.</returns> /// <exception cref="ArgumentNullException">When <paramref name="context" /> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">When <paramref name="property" /> is <c>null</c>.</exception> /// <exception cref="ArgumentException">When the <see cref="MemberInfo.Name" /> of <paramref name="context" /> is not a valid property.</exception> private static P GetMutatedProperty <T, P>(MutationContext <T> context, PropertyInfo property, bool inferValue, P value = default(P)) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (property == null) { throw new ArgumentNullException(nameof(property)); } if (inferValue) { value = (P)property.GetValue(context.ObjectInstance); } // Throw if a value cannot be assigned to this property. EnsureValidPropertyType(property.Name, AttributeStore.Instance.GetPropertyType(context, property), value); // Create a new context using the existing context's IServiceProvider and items, so that we will not overwrite the properties of the existing instance. var propertyContext = new MutationContext <T>(context.ObjectInstance, AttributeStore.Instance.GetPropertyAttributes(context, property), context.Items, context); var attributes = propertyContext.Attributes.OfType <MutationAttribute>(); if (attributes.Any()) { value = GetMutatedValue(propertyContext, attributes, value); if (inferValue && CanBeAssigned(property.PropertyType, value)) { property.SetValue(context.ObjectInstance, value); } } return(value); }
/// <summary> /// Mutates the specified instance against the current context and the specified <see cref="MutationAttribute" />s. /// </summary> /// <typeparam name="T">The type to consult during mutation.</typeparam> /// <param name="context">Describes the type of object being mutated and provides services and context for mutation.</param> /// <param name="instance">The instance to be modified.</param> /// <param name="attributes">The list of <see cref="MutationAttribute" />s to modify the specified <paramref name="instance" /> against.</param> /// <returns>The object whose value and properties has been modified according to any associated <see cref="MutationAttribute" />s and <see cref="IMutableObject" /> implementation.</returns> /// <exception cref="ArgumentNullException">When <paramref name="context" /> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">When <paramref name="attributes" /> is <c>null</c>.</exception> public static T Mutate <T>(this MutationContext <T> context, T instance, IEnumerable <MutationAttribute> attributes) where T : class => GetMutatedObject(context, attributes, false, instance);
/// <summary> /// Mutates the specified instance against the current context. /// </summary> /// <typeparam name="T">The type to consult during mutation.</typeparam> /// <param name="context">Describes the type of object being mutated and provides services and context for mutation.</param> /// <param name="instance">The instance to be modified.</param> /// <returns>The object whose value and/or properties has been modified according to any associated <see cref="MutationAttribute" />s and <see cref="IMutableObject" /> implementation.</returns> /// <exception cref="ArgumentNullException">When <paramref name="context" /> is <c>null</c>.</exception> public static T Mutate <T>(this MutationContext <T> context, T instance) where T : class => GetMutatedObject(context, false, instance);
/// <summary> /// Mutates the instance associated with the current context. /// </summary> /// <typeparam name="T">The type to consult during mutation.</typeparam> /// <param name="context">Describes the type of object being mutated and provides services and context for mutation.</param> /// <returns>The object whose value and properties has been modified according to any associated <see cref="MutationAttribute" />s and <see cref="IMutableObject" /> implementation.</returns> /// <exception cref="ArgumentNullException">When <paramref name="context" /> is <c>null</c>.</exception> public static T Mutate <T>(this MutationContext <T> context) where T : class => GetMutatedObject(context, true);
/// <summary> /// Mutates the specified value against the current context and the specified <see cref="MutationAttribute" />s. /// </summary> /// <typeparam name="T">The type to consult during mutation.</typeparam> /// <param name="context">Describes the type of object being mutated and provides services and context for mutation.</param> /// <param name="attributes">The list of <see cref="MutationAttribute" />s to modify the specified <paramref name="value" /> against.</param> /// <param name="value">The value to be mutated.</param> /// <returns>The value that has been modified according to any associated <see cref="MutationAttribute" />s.</returns> /// <exception cref="ArgumentNullException">When <paramref name="context" /> is required and <c>null</c>.</exception> /// <exception cref="ArgumentNullException">When <paramref name="attributes" /> is <c>null</c>.</exception> public static T Mutate <T>(this MutationContext <T> context, IEnumerable <MutationAttribute> attributes, T value) where T : struct => GetMutatedValue(context, attributes, value);
/// <summary> /// Mutates the value or instance associated with the current context and the specified <see cref="MutationAttribute" />s. /// </summary> /// <remarks> /// When the consulting type, specified by <typeparamref name="T" />, is a reference type, the instance and its properties will be mutated. Likewise, only a value type's value will be mutated. /// <para> /// The <see cref="MutationAttribute" />s specified in <paramref name="attributes" /> will only be used to mutate the specified value or instance. Any properties that are mutated will be mutated according to their respective <see cref="MutationAttribute" />s. /// </para> /// </remarks> /// <typeparam name="T">The type to consult during mutation.</typeparam> /// <param name="context">Describes the type of object being mutated and provides services and context for mutation.</param> /// <param name="attributes">The list of <see cref="MutationAttribute" />s to modify the specified <see cref="MutationContext{T}.ObjectInstance" /> against.</param> /// <returns>The object whose value and/or properties has been modified according to any associated <see cref="MutationAttribute" />s and <see cref="IMutableObject" /> implementation.</returns> /// <exception cref="ArgumentNullException">When <typeparamref name="T" /> is a value type, <paramref name="context" /> is required, and <c>null</c>; or when <paramref name="context" /> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">When <paramref name="attributes" /> is <c>null</c>.</exception> public static T Mutate <T>(this MutationContext <T> context, IEnumerable <MutationAttribute> attributes) => typeof(T).GetTypeInfo().IsValueType ? GetMutatedValue(context, attributes, context == null ? default(T) : context.ObjectInstance) : GetMutatedObject(context, attributes, true);
/// <summary> /// Mutates an object instance against the current context. /// </summary> /// <typeparam name="T">The type to consult during mutation.</typeparam> /// <param name="context">Describes the type of object being mutated and provides services and context for mutation.</param> /// <param name="inferValue">If <c>true</c>, the <see cref="MutationContext{T}.ObjectInstance" /> will be mutated in place of specified <paramref name="instance" />.</param> /// <param name="instance">The instance to be modified.</param> /// <returns>The object whose value and properties has been modified according to any associated <see cref="MutationAttribute" />s and <see cref="IMutableObject" /> implementation.</returns> /// <exception cref="ArgumentNullException">When <paramref name="context" /> is <c>null</c>.</exception> private static T GetMutatedObject <T>(MutationContext <T> context, bool inferValue, T instance = default(T)) => GetMutatedObject(context, context?.Attributes?.OfType <MutationAttribute>(), inferValue, instance);
/// <summary> /// Mutates the specified value against the specified property of the instance associated with the current context. /// </summary> /// <typeparam name="T">The type to consult during mutation.</typeparam> /// <typeparam name="P">The property type to consult for <see cref="MutationAttribute" />s.</typeparam> /// <param name="context">Describes the type of object being mutated and provides services and context for mutation.</param> /// <param name="property">The expression that selects the property that describes the <paramref name="value" /> to be modified.</param> /// <param name="value">The value to be mutated.</param> /// <returns>The value that has been modified according to any <see cref="MutationAttribute" />s associated with the specified property.</returns> /// <exception cref="ArgumentNullException">When <paramref name="context" /> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">When <paramref name="property" /> is <c>null</c>.</exception> /// <exception cref="ArgumentException">When the expression doesn't indicate a valid <paramref name="property" />.</exception> /// <exception cref="ArgumentException">When the <see cref="MemberInfo.Name" /> of <paramref name="context" /> is not a valid property.</exception> public static P MutateProperty <T, P>(this MutationContext <T> context, Expression <Func <T, P> > property, P value) where T : class => GetMutatedProperty(context, GetPropertyInfo(property), false, value);
/// <summary> /// Mutates the specified value against the specified property of the instance associated with the current context. /// </summary> /// <typeparam name="T">The type to consult during mutation.</typeparam> /// <typeparam name="P">The property type to consult for <see cref="MutationAttribute" />s.</typeparam> /// <param name="context">Describes the type of object being mutated and provides services and context for mutation.</param> /// <param name="property">The property info that describes the <paramref name="value" /> to be modified.</param> /// <param name="value">The value to be mutated.</param> /// <returns>The value that has been modified according to any <see cref="MutationAttribute" />s associated with the specified property.</returns> /// <exception cref="ArgumentNullException">When <paramref name="context" /> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">When <paramref name="property" /> is <c>null</c>.</exception> /// <exception cref="ArgumentException">When the <see cref="MemberInfo.Name" /> of <paramref name="context" /> is not a valid property.</exception> public static P MutateProperty <T, P>(this MutationContext <T> context, PropertyInfo property, P value) where T : class => GetMutatedProperty(context, property, false, value);
/// <summary> /// Mutates the specified property of the instance associated with the current context. /// </summary> /// <typeparam name="T">The type to consult during mutation.</typeparam> /// <param name="context">Describes the type of object being mutated and provides services and context for mutation.</param> /// <param name="property">The property info that describes the member to be modified.</param> /// <returns>The property value that has been modified according to any associated <see cref="MutationAttribute" />s.</returns> /// <exception cref="ArgumentNullException">When <paramref name="context" /> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">When <paramref name="property" /> is <c>null</c>.</exception> /// <exception cref="ArgumentException">When the <see cref="MemberInfo.Name" /> of <paramref name="context" /> is not a valid property.</exception> public static object MutateProperty <T>(this MutationContext <T> context, PropertyInfo property) where T : class => GetMutatedProperty <T, object>(context, property, true);