/// <summary>
        /// Traverses object graph, flattens and converts all nested (and not nested) properties to EntityProperties, stores them in the property dictionary.
        /// The keys are constructed by appending the names of the properties visited during pre-order depth first traversal from root to each end node property delimited by '_'.
        /// Allows complex objects to be stored in persistent storage systems or passed between web services in a generic way.
        /// </summary>
        /// <param name="root">The object to flatten and convert.</param>
        /// <param name="entityPropertyConverterOptions">A <see cref="EntityPropertyConverterOptions"/> object that specifies options for the entity property conversion.</param>
        /// <param name="operationContext">An <see cref="OperationContext"/> object that represents the context for the current operation.</param>
        /// <returns>The result containing <see cref="IDictionary{TKey,TValue}"/> of <see cref="EntityProperty"/> objects for all properties of the flattened root object.</returns>
        public static Dictionary <string, EntityProperty> Flatten(object root, EntityPropertyConverterOptions entityPropertyConverterOptions, OperationContext operationContext)
        {
            if (root == null)
            {
                return(null);
            }

            Dictionary <string, EntityProperty> propertyDictionary = new Dictionary <string, EntityProperty>();
            HashSet <object> antecedents = new HashSet <object>(new ObjectReferenceEqualityComparer());

            return(Flatten(propertyDictionary, root, string.Empty, antecedents, entityPropertyConverterOptions, operationContext) ? propertyDictionary : null);
        }
        /// <summary>
        /// Reconstructs the complete object graph of type T using the flattened entity property dictionary and returns reconstructed object.
        /// The property dictionary may contain only basic properties, only nested properties or a mix of both types.
        /// </summary>
        /// <typeparam name="T">The type of the object to populate</typeparam>
        /// <param name="flattenedEntityProperties">The flattened entity property dictionary.</param>
        /// <param name="entityPropertyConverterOptions">A <see cref="EntityPropertyConverterOptions"/> object that specifies options for the entity property conversion.</param>
        /// <param name="operationContext">An <see cref="OperationContext"/> object that represents the context for the current operation.</param>
        /// <returns>The result containing the reconstructed object with its full object hierarchy.</returns>
        public static T ConvertBack <T>(
            IDictionary <string, EntityProperty> flattenedEntityProperties,
            EntityPropertyConverterOptions entityPropertyConverterOptions,
            OperationContext operationContext)
        {
            if (flattenedEntityProperties == null)
            {
                return(default(T));
            }

#if WINDOWS_DESKTOP || WINDOWS_RT || NETCORE
            T root = (T)Activator.CreateInstance(typeof(T));
#else
            T root = (T)FormatterServices.GetUninitializedObject(typeof(T));
#endif

            return(flattenedEntityProperties.Aggregate(root, (current, kvp) => (T)SetProperty(current, kvp.Key, kvp.Value.PropertyAsObject, entityPropertyConverterOptions, operationContext)));
        }
        /// <summary>Sets the property given with the property path on the passed in object.</summary>
        /// <param name="root">The root object.</param>
        /// <param name="propertyPath">The full property path formed by the name of properties from root object to the target property(included), appended by '.'.</param>
        /// <param name="propertyValue">The property value.</param>
        /// <param name="entityPropertyConverterOptions">A <see cref="EntityPropertyConverterOptions"/> object that specifies options for the entity property conversion.</param>
        /// <param name="operationContext">An <see cref="OperationContext"/> object that represents the context for the current operation.</param>
        /// <returns>The updated <see cref="object"/>.</returns>
        private static object SetProperty(
            object root,
            string propertyPath,
            object propertyValue,
            EntityPropertyConverterOptions entityPropertyConverterOptions,
            OperationContext operationContext)
        {
            if (root == null)
            {
                throw new ArgumentNullException("root");
            }

            if (propertyPath == null)
            {
                throw new ArgumentNullException("propertyPath");
            }

            try
            {
                string propertyNameDelimiter = entityPropertyConverterOptions != null ? entityPropertyConverterOptions.PropertyNameDelimiter : DefaultPropertyNameDelimiter;

                Stack <Tuple <object, object, PropertyInfo> > valueTypePropertyHierarchy = new Stack <Tuple <object, object, PropertyInfo> >();
                string[] properties = propertyPath.Split(new[] { propertyNameDelimiter }, StringSplitOptions.RemoveEmptyEntries);

                object parentProperty    = root;
                bool   valueTypeDetected = false;

                for (int i = 0; i < properties.Length - 1; i++)
                {
#if WINDOWS_RT || NETCORE
                    PropertyInfo propertyToGet = parentProperty.GetType().GetRuntimeProperty(properties[i]);
#else
                    PropertyInfo propertyToGet = parentProperty.GetType().GetProperty(properties[i]);
#endif
                    object temp = propertyToGet.GetValue(parentProperty, null);
                    Type   type = propertyToGet.PropertyType;

                    if (temp == null)
                    {
#if WINDOWS_DESKTOP || WINDOWS_RT || NETCORE
                        temp = Activator.CreateInstance(type);
#else
                        temp = FormatterServices.GetUninitializedObject(type);
#endif
                        propertyToGet.SetValue(parentProperty, ChangeType(temp, propertyToGet.PropertyType), index: null);
                    }

#if WINDOWS_RT || NETCORE
                    if (valueTypeDetected || type.GetTypeInfo().IsValueType)
#else
                    if (valueTypeDetected || type.IsValueType)
#endif
                    {
                        valueTypeDetected = true;
                        valueTypePropertyHierarchy.Push(new Tuple <object, object, PropertyInfo>(temp, parentProperty, propertyToGet));
                    }

                    parentProperty = temp;
                }

#if WINDOWS_RT || NETCORE
                PropertyInfo propertyToSet = parentProperty.GetType().GetRuntimeProperty(properties.Last());
#else
                PropertyInfo propertyToSet = parentProperty.GetType().GetProperty(properties.Last());
#endif
                propertyToSet.SetValue(parentProperty, ChangeType(propertyValue, propertyToSet.PropertyType), index: null);

                object termValue = parentProperty;
                while (valueTypePropertyHierarchy.Count != 0)
                {
                    Tuple <object, object, PropertyInfo> propertyTuple = valueTypePropertyHierarchy.Pop();
                    propertyTuple.Item3.SetValue(propertyTuple.Item2, ChangeType(termValue, propertyTuple.Item3.PropertyType), index: null);
                    termValue = propertyTuple.Item2;
                }

                return(root);
            }
            catch (Exception ex)
            {
                Logger.LogError(operationContext, SR.TraceSetPropertyError, propertyPath, propertyValue, ex.Message);
                throw;
            }
        }
        /// <summary>
        /// Traverses object graph, flattens and converts all nested (and not nested) properties to EntityProperties, stores them in the property dictionary.
        /// The keys are constructed by appending the names of the properties visited during pre-order depth first traversal from root to each end node property delimited by '.'.
        /// Allows complex objects to be stored in persistent storage systems or passed between web services in a generic way.
        /// </summary>
        /// <param name="propertyDictionary">The property dictionary.</param>
        /// <param name="current">The current object.</param>
        /// <param name="objectPath">The object path.</param>
        /// <param name="antecedents">The antecedents of current object, used to detect circular references in object graph.</param>
        /// <param name="entityPropertyConverterOptions">A <see cref="EntityPropertyConverterOptions"/> object that specifies options for the entity property conversion.</param>
        /// <param name="operationContext">An <see cref="OperationContext"/> object that represents the context for the current operation.</param>
        /// <returns>The <see cref="bool"/> to indicate success of conversion to flattened EntityPropertyDictionary.</returns>
        private static bool Flatten(
            Dictionary <string, EntityProperty> propertyDictionary,
            object current,
            string objectPath,
            HashSet <object> antecedents,
            EntityPropertyConverterOptions entityPropertyConverterOptions,
            OperationContext operationContext)
        {
            if (current == null)
            {
                return(true);
            }

            Type           type           = current.GetType();
            EntityProperty entityProperty = CreateEntityPropertyWithType(current, type);

            if (entityProperty != null)
            {
                propertyDictionary.Add(objectPath, entityProperty);
                return(true);
            }

#if WINDOWS_RT
            IEnumerable <PropertyInfo> propertyInfos = type.GetRuntimeProperties();
#elif NETCORE
            IEnumerable <PropertyInfo> propertyInfos = type.GetTypeInfo().GetAllProperties();
#else
            IEnumerable <PropertyInfo> propertyInfos = type.GetProperties();
#endif
            if (!propertyInfos.Any())
            {
                throw new SerializationException(string.Format(CultureInfo.InvariantCulture, SR.UnsupportedPropertyTypeForEntityPropertyConversion, type, objectPath));
            }

            bool isAntecedent = false;

#if WINDOWS_RT || NETCORE
            if (!type.GetTypeInfo().IsValueType)
#else
            if (!type.IsValueType)
#endif
            {
                if (antecedents.Contains(current))
                {
                    throw new SerializationException(string.Format(CultureInfo.InvariantCulture, SR.RecursiveReferencedObject, objectPath, type));
                }

                antecedents.Add(current);
                isAntecedent = true;
            }

            string propertyNameDelimiter = entityPropertyConverterOptions != null ? entityPropertyConverterOptions.PropertyNameDelimiter : DefaultPropertyNameDelimiter;

            bool success = propertyInfos
                           .Where(propertyInfo => !ShouldSkip(propertyInfo, objectPath, operationContext))
                           .All(propertyInfo =>
            {
                if (propertyInfo.Name.Contains(propertyNameDelimiter))
                {
                    throw new SerializationException(
                        string.Format(CultureInfo.InvariantCulture, SR.PropertyDelimiterExistsInPropertyName, propertyNameDelimiter, propertyInfo.Name, objectPath));
                }

                return(Flatten(
                           propertyDictionary,
                           propertyInfo.GetValue(current, index: null),
                           string.IsNullOrWhiteSpace(objectPath) ? propertyInfo.Name : objectPath + propertyNameDelimiter + propertyInfo.Name,
                           antecedents,
                           entityPropertyConverterOptions,
                           operationContext));
            });

            if (isAntecedent)
            {
                antecedents.Remove(current);
            }

            return(success);
        }
예제 #5
0
        /// <summary>
        /// Flattens the entity and creates a <see cref="IDictionary{TKey,TValue}"/> of <see cref="EntityProperty"/> objects for all properties of the specified entity object.
        /// </summary>
        /// <param name="entity">The entity object to serialize.</param>
        /// <param name="entityPropertyConverterOptions">A <see cref="EntityPropertyConverterOptions"/> object that specifies options for the entity property conversion.</param>
        /// <param name="operationContext">An <see cref="OperationContext"/> object that represents the context for the current operation.</param>
        /// <returns>An <see cref="IDictionary{TKey,TValue}"/> of <see cref="EntityProperty"/> objects for all the properties of the specified entity object.</returns>
        /// <remarks>The entity type can be a simple object with a flat structure or a complex object with complex properties and multiple levels of object hierarchy.
        /// Generic ConvertBack method can recompose the original entity using the return value of this method.</remarks>
        public static IDictionary <string, EntityProperty> Flatten(object entity, EntityPropertyConverterOptions entityPropertyConverterOptions, OperationContext operationContext)
        {
            CommonUtility.AssertNotNull("entity", entity);

            return(EntityPropertyConverter.Flatten(entity, entityPropertyConverterOptions, operationContext));
        }
예제 #6
0
 /// <summary>
 /// Returns a custom entity instance which is recomposed using the specified <see cref="IDictionary{TKey,TValue}"/> of property names to <see cref="EntityProperty"/> data typed values.
 /// </summary>
 /// <typeparam name="TResult">The type of the recomposed object. This can be a simple object with a flat structure or a complex object with complex properties and multiple levels of object hierarchy.</typeparam>
 /// <param name="properties">An <see cref="IDictionary{TKey,TValue}"/> object that maps string property names to <see cref="EntityProperty"/> data values to deserialize and store in this table entity instance.</param>
 /// <param name="entityPropertyConverterOptions">A <see cref="EntityPropertyConverterOptions"/> object that specifies options for the entity property conversion.</param>
 /// <param name="operationContext">An <see cref="OperationContext"/> object that represents the context for the current operation.</param>
 public static TResult ConvertBack <TResult>(IDictionary <string, EntityProperty> properties, EntityPropertyConverterOptions entityPropertyConverterOptions, OperationContext operationContext)
 {
     return(EntityPropertyConverter.ConvertBack <TResult>(properties, entityPropertyConverterOptions, operationContext));
 }