/// <summary> /// Attempts to drill into the supplied type and remove the next found wrapper (given the provided filters) /// to distill it to its core type. (i.e. convert IEnumerable[T] to 'T'). /// </summary> /// <param name="type">The type to inspect.</param> /// <param name="eliminateEnumerables">if set to <c>true</c> this method will attempt to eliminate any wrapper type that declares <see cref="IEnumerable{T}" />.</param> /// <param name="eliminateTask">if set to <c>true</c> this method will attempt to remove a <see cref="Task{T}" /> declaration from the type.</param> /// <param name="eliminateNullableT">if set to <c>true</c> the Nullable{T} wrapper that can exist on value types will be stripped (leaving just T).</param> /// <returns>Type.</returns> public static Type EliminateNextWrapperFromCoreType( Type type, bool eliminateEnumerables = true, bool eliminateTask = true, bool eliminateNullableT = true) { if (type == null) { return(null); } // eliminate Task<T> if (eliminateTask && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task <>)) { return(type.GetGenericArguments()[0]); } // drill into the concrete type if a list was supplied List<T>, IEnumerable<T>, ICollection<T> etc. if (eliminateEnumerables && GraphValidation.IsValidListType(type)) { return(type.GetEnumerableUnderlyingType(false)); } if (eliminateNullableT && Validation.IsNullableOfT(type)) { return(Nullable.GetUnderlyingType(type)); } return(type); }
/// <summary> /// Inspects the provided type and generates a type expression to represent it in the object grpah. /// </summary> /// <param name="typeToCheck">The complete type specification to check.</param> /// <param name="typeDefinition">An optional expression declaration to use as a set of overrides on the type provided.</param> /// <returns>GraphFieldOptions.</returns> public static GraphTypeExpression GenerateTypeExpression(Type typeToCheck, IGraphTypeExpressionDeclaration typeDefinition = null) { Validation.ThrowIfNull(typeToCheck, nameof(typeToCheck)); if (typeDefinition?.TypeWrappers != null) { typeToCheck = GraphValidation.EliminateWrappersFromCoreType( typeToCheck, eliminateEnumerables: true, eliminateTask: true, eliminateNullableT: true); return(new GraphTypeExpression(typeToCheck.FriendlyName(), typeDefinition.TypeWrappers)); } // strip out Task{T} before doin any type inspections typeToCheck = GraphValidation.EliminateWrappersFromCoreType( typeToCheck, eliminateEnumerables: false, eliminateTask: true, eliminateNullableT: false); var hasDefinedDefaultValue = typeDefinition?.HasDefaultValue ?? false; List <MetaGraphTypes> wrappers = new List <MetaGraphTypes>(); if (GraphValidation.IsValidListType(typeToCheck)) { // auto generated type expressions will always allow for a nullable list (since class references can be null) // unwrap any nested lists as necessary: e.g. IEnumerable<IEnumerable<IEnumerable<T>>> while (true) { var unwrappedType = EliminateNextWrapperFromCoreType( typeToCheck, eliminateEnumerables: true, eliminateTask: false, eliminateNullableT: false); if (unwrappedType == typeToCheck) { break; } wrappers.Add(MetaGraphTypes.IsList); typeToCheck = unwrappedType; } if (!hasDefinedDefaultValue && GraphValidation.IsNotNullable(typeToCheck)) { wrappers.Add(MetaGraphTypes.IsNotNull); } } else if (!hasDefinedDefaultValue && GraphValidation.IsNotNullable(typeToCheck)) { wrappers.Add(MetaGraphTypes.IsNotNull); } typeToCheck = EliminateWrappersFromCoreType( typeToCheck, eliminateEnumerables: false, eliminateTask: false, eliminateNullableT: true); return(new GraphTypeExpression(typeToCheck.FriendlyName(), wrappers)); }