public EnumerableBuilder(Builder <T> singleObjBuilder) { SingleObjBuilder = singleObjBuilder ?? throw new ArgumentNullException(nameof(singleObjBuilder)); var values = Expression.Parameter(typeof(IEnumerable <>).MakeGenericType(new [] { typeof(T) })); var(isCollection, createExpression) = Enumerables.CreateCollectionExpression( typeof(TCollection), values); // will not occur because of generic constraint if (!isCollection) { throw new InvalidOperationException($"Type {typeof(TCollection)} must be an IEnumerable<>"); } CollectionBuilder = EnsureCollectionIsNotIEnumerable(Expression .Lambda <Func <IEnumerable <T>, TCollection> >(createExpression, values)) .Compile(); }
/// <summary> /// Get a function which converts from IEnumerable -> propertyType. If no function found, falls back to casting /// </summary> static Func <IEnumerable, ILogger, TCollection> BuildEnumerableConvertor <TCollection, T>(bool cellTypeIsEnumerable) //bool resultIsCollectionOfCellsCCC, bool cellTypeIsEnumerableCCC) where TCollection : IEnumerable <T> { var innerConvertor = GetConvertor <T>(cellTypeIsEnumerable); var createCollectionInput = Expression.Parameter(typeof(IEnumerable <T>)); var(isCollection, builder) = Enumerables.CreateCollectionExpression(typeof(TCollection), createCollectionInput); if (!isCollection) { throw new InvalidOperationException($"Unable to create collection expressoin for collection: {typeof(TCollection)}"); } var createCollection = Expression .Lambda <Func <IEnumerable <T>, TCollection> >(builder, createCollectionInput) .Compile(); var collectionIsNullable = typeof(TCollection).IsInterface || !typeof(TCollection).IsValueType || ReflectionUtils.IsNullable(typeof(TCollection)); var innerIsEnumerable = ReflectionUtils.GetIEnumerableType(typeof(T)) != null; return(Convert); TCollection Convert(IEnumerable input, ILogger logger) { if (input is TCollection) { return((TCollection)input); } if (input == null || DBNull.Value.Equals(input)) { if (collectionIsNullable) { return(default(TCollection)); } throw new InvalidOperationException($"Cannot use value of null for type {typeof(TCollection)}"); } if (cellTypeIsEnumerable && !innerIsEnumerable && logger.CanLogWarning(LogMessages.InefficientCastWarning)) { var valsType = GetTypeString(input); logger.LogWarning($"Converting {valsType} to type {typeof(TCollection)}. " + $"This conversion is inefficient. Consider changing the " + $"result data type to {valsType}", LogMessages.InefficientCastWarning); /*original message: * $"Converting {valsType} to type {collectionType} for property " + * $"\"{propertyName}\". This conversion is inefficient. Consider changing the " + * $"data type of \"{propertyName}\" to {valsType}" */ } // ToArray ensures that properties of type IEnumerable<T> will // have a concrete type (and not an enumerator) var converted = ConvertToType(input, logger).ToArray(); return(createCollection(converted)); } IEnumerable <T> ConvertToType(IEnumerable values, ILogger logger) { foreach (var val in values) { yield return(innerConvertor(val, logger)); } } }