public static void Approve <T>(IEnumerable <T> enumerable, EnumerableWriter.CustomFormatter <T> formatter) { Approve(EnumerableWriter.Write(enumerable, formatter)); }
public static void Approve <T>(String header, IEnumerable <T> enumerable, EnumerableWriter.CustomFormatter <T> formatter) { Approve(header + "\r\n\r\n" + EnumerableWriter.Write(enumerable, formatter)); }
/// <summary> /// Copies all properties of the provided source-instance onto the provided destination-instance. /// </summary> /// <param name="sourceType">Type of the source value.</param> /// <param name="destinationType">Type of the destination value.</param> /// <param name="source">Source-value to copy.</param> /// <param name="destination">Target-value which properties will be copied onto.</param> /// <param name="throwOnMismatch"> /// If true, throws when there is a mismatch between the types being converted. /// If false, silently ignores property with mismatch and moves along to the next one. /// </param> /// <param name="ignorePropertiesWithoutSetter"> /// Will ignore properties that has only a get function /// </param> public void CopyProperties(Type sourceType, Type destinationType, object source, object destination, bool throwOnMismatch, bool ignorePropertiesWithoutSetter = false) { // TODO: future bugfix/optimization // - add "stack"/repository for copied instances, so that circuler references will not cause isssues. // - will probably also save CPU-cycles by not having to copy same instance several times. // (instance-repository needs to follow copy-process, not SoMapper instance.) /* * Person p; * Contact c = p.Contact; * Person[] ps = c.Persons // includes p, which includes c, etc. */ Type type1 = sourceType; Type type2 = destinationType; // just get the properties we can work with, nothing more. const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public; PropertyInfo[] type1PropertyInfos = type1.GetProperties(flags).Where(p => p.CanRead).ToArray(); PropertyInfo[] type2PropertyInfos = type2.GetProperties(flags).Where(p => p.CanWrite).ToArray(); PropertyInfo[] type2PropertyInfosCanRead = type2.GetProperties(flags).Where(p => p.CanRead).ToArray(); // process properties foreach (var type1PropertyInfo in type1PropertyInfos) { // do null check before other analysis object type1PropertyValue = type1PropertyInfo.GetValue(source, null); if (type1PropertyValue == null) { // no point processing null-values. continue; } // find target prop var type2PropertyInfo = type2PropertyInfos.FirstOrDefault(pi => pi.Name == type1PropertyInfo.Name); if (type2PropertyInfo == null) { if (throwOnMismatch) { var type2PropertyInfoCanRead = type2PropertyInfosCanRead.FirstOrDefault(pi => pi.Name == type1PropertyInfo.Name); if (ignorePropertiesWithoutSetter && type2PropertyInfoCanRead != null) { continue; } else { string message = string.Format( "Cannot transfer property '{0}' from type '{1}' to type '{2}': Property does not exist on target type.", type1PropertyInfo.Name, type1.FullName, type2.FullName); throw new InvalidOperationException(message); } } else { continue; } } // validate match between preoprties Type type1PropertyType = type1PropertyInfo.PropertyType; Type type2PropertyType = type2PropertyInfo.PropertyType; Type type1ElementType, type2ElementType; bool type1IsEnumerable = type1PropertyType.IsEnumerable(out type1ElementType); bool type2IsEnumerable = type2PropertyType.IsEnumerable(out type2ElementType); // both must be array or not bool isCoherent1 = type1IsEnumerable == type2IsEnumerable; if (!isCoherent1) { if (throwOnMismatch) { string message = string.Format( "Cannot transfer property '{0}' from type '{1}' to type '{2}': Cannot transfer value between enumerable and non-enumerable types.", type1PropertyInfo.Name, type1.FullName, type2.FullName); throw new InvalidOperationException(message); } else { // next property continue; } } // validate array-rank only if array. if (type1IsEnumerable && type1.IsArray) { int rank = type1PropertyType.GetArrayRank(); if (rank != 1) { // throw. always. this is not a "mismatch" throw new NotSupportedException("Duck-copying multidimensional arrays is not supported."); } } // validate type-conversion Type type1ValidationType, type2ValidationType; if (type1IsEnumerable) { type1ValidationType = type1ElementType; type2ValidationType = type2ElementType; } else { type1ValidationType = type1PropertyType; type2ValidationType = type2PropertyType; } bool type1IsPrimitive = type1ValidationType.IsSimple(); bool type2IsPrimitive = type2ValidationType.IsSimple(); bool type1IsDecimal = type1ValidationType.IsDecimalNumber(); bool type2IsDecimal = type2ValidationType.IsDecimalNumber(); bool type1IsEnum = type1ValidationType.IsEnum; bool type2IsEnum = type2ValidationType.IsEnum; bool bothAreSamePrimitive = (type1IsPrimitive && type2IsPrimitive && (type1ValidationType == type2ValidationType)); bool bothAreNonPrimitives = (!type1IsPrimitive && !type2IsPrimitive); bool bothAreDecimals = (type1IsDecimal && type2IsDecimal); bool bothAreEnum = type1IsEnum && type2IsEnum; bool isCoherent2 = bothAreNonPrimitives || bothAreSamePrimitive || bothAreEnum || bothAreDecimals; if (!isCoherent2) { if (throwOnMismatch) { string message = string.Format( "Cannot transfer property '{0}' from type '{1}' to type '{2}': Properties are not coherent across types.", type1PropertyInfo.Name, type1.FullName, type2.FullName); throw new InvalidOperationException(message); } else { // next property continue; } } // handle everything as Enumerables to simplify codebase System.Collections.IEnumerable sourceEnumerable; if (!type1IsEnumerable) { sourceEnumerable = new[] { type1PropertyValue }; // fake a collection-type destination to have the activator create something to insert data into. type2PropertyType = typeof(IEnumerable <>).MakeGenericType(type2ValidationType); } else { sourceEnumerable = (System.Collections.IEnumerable)type1PropertyValue; } // safely allocate target collection System.Collections.IEnumerable destinationEnumerable = (System.Collections.IEnumerable)CreateDefaultEntity(type2PropertyType); EnumerableWriter writer = EnumerableWriter.CreateFor(type2PropertyType); // do set-conversion foreach (object sourceValue in sourceEnumerable) { object destinationValue; if (type1IsPrimitive && bothAreSamePrimitive) { // primitive types can be copied directly. destinationValue = sourceValue; } else if (type1IsPrimitive && bothAreDecimals) { destinationValue = CopyDecimal(sourceValue, type2ValidationType); } else if (type1IsPrimitive) { throw new InvalidOperationException("Unsupported property-copy between non-compatible primitive types."); } else if (type1ValidationType == type2ValidationType && type1ValidationType.IsInterface) { // we cannot reliably create new instances of these, but we can safely copy instance. destinationValue = sourceValue; } else if (bothAreEnum) { destinationValue = Enum.ToObject(type2ValidationType, (int)sourceValue); } else { // complex types needs to be converted by doing a recursive self invocation. // NOTE: we specify validation-types to ensure it works for both enumerables and non-enumerables. destinationValue = CreateDefaultEntity(type2ValidationType); CopyProperties(type1ValidationType, type2ValidationType, sourceValue, destinationValue, throwOnMismatch); } destinationEnumerable = writer.Write(destinationEnumerable, destinationValue); } object result; if (type1IsEnumerable) { result = destinationEnumerable; } else { // we need to unwrap our value from the enumerator System.Collections.IEnumerator enumerator = destinationEnumerable.GetEnumerator(); if (enumerator.MoveNext()) { result = enumerator.Current; } else { // this should never happen. so we throw. throw new Exception("Internal error copying single value between types."); } } // all that remains is writing copy. type2PropertyInfo.SetValue(destination, result, null); } }
public static void Approve <T>(IEnumerable <T> enumerable, string label) { Approve(EnumerableWriter.Write(enumerable, label)); }
public string ExecuteQuery(string query) { return(EnumerableWriter.Write(GetQueryExtracted(), "author")); }