/// <summary> /// Initializes an instance of <see cref="EnumerableWrapperProvider"/>. /// </summary> /// <param name="sourceEnumerableOfT">Type of the original <see cref="IEnumerable{T}" /> /// that is being wrapped.</param> /// <param name="elementWrapperProvider">The <see cref="IWrapperProvider"/> for the element type. /// Can be null.</param> public EnumerableWrapperProvider( Type sourceEnumerableOfT, IWrapperProvider elementWrapperProvider) { if (sourceEnumerableOfT == null) { throw new ArgumentNullException(nameof(sourceEnumerableOfT)); } var enumerableOfT = ClosedGenericMatcher.ExtractGenericInterface( sourceEnumerableOfT, typeof(IEnumerable <>)); if (!sourceEnumerableOfT.GetTypeInfo().IsInterface || enumerableOfT == null) { throw new ArgumentException($"The type must be an interface and must be or derive from '{typeof(IEnumerable<>).Name}'.", nameof(sourceEnumerableOfT)); } _wrapperProvider = elementWrapperProvider; var declaredElementType = enumerableOfT.GenericTypeArguments[0]; var wrappedElementType = elementWrapperProvider?.WrappingType ?? declaredElementType; WrappingType = typeof(DelegatingEnumerable <,>).MakeGenericType(wrappedElementType, declaredElementType); _wrappingTypeConstructor = WrappingType.GetConstructor(new[] { sourceEnumerableOfT, typeof(IWrapperProvider) }); }
protected virtual bool TryGetListTypeArgument(IList list, out Type listTypeArgument, out string errorMessage) { // Arrays are not supported as they have fixed size and operations like Add, Insert do not make sense var listType = list.GetType(); if (listType.IsArray) { errorMessage = Resources.FormatPatchNotSupportedForArrays(listType.FullName); listTypeArgument = null; return(false); } else { var genericList = ClosedGenericMatcher.ExtractGenericInterface(listType, typeof(IList <>)); if (genericList == null) { errorMessage = Resources.FormatPatchNotSupportedForNonGenericLists(listType.FullName); listTypeArgument = null; return(false); } else { listTypeArgument = genericList.GenericTypeArguments[0]; errorMessage = null; return(true); } } }
private static HandlerMethod CreateHandlerMethod(HandlerMethodDescriptor handlerDescriptor) { var method = handlerDescriptor.MethodInfo; var parameters = handlerDescriptor.Parameters.ToArray(); var returnType = method.ReturnType; if (returnType == typeof(void)) { return(new VoidHandlerMethod(parameters, method)); } else if (typeof(IActionResult).IsAssignableFrom(returnType)) { return(new ActionResultHandlerMethod(parameters, method)); } else if (returnType == typeof(Task)) { return(new NonGenericTaskHandlerMethod(parameters, method)); } else { var taskType = ClosedGenericMatcher.ExtractGenericInterface(returnType, typeof(Task <>)); if (taskType != null && typeof(IActionResult).IsAssignableFrom(taskType.GenericTypeArguments[0])) { return(new GenericTaskHandlerMethod(parameters, method)); } } throw new InvalidOperationException(Resources.FormatUnsupportedHandlerMethodType(returnType)); }
/// <summary> /// Attempts to produces a delegate that reads an <see cref="IAsyncEnumerable{T}"/> into an <see cref="ICollection{T}"/>. /// </summary> /// <param name="type">The type to read.</param> /// <param name="reader">A delegate that when awaited reads the <see cref="IAsyncEnumerable{T}"/>.</param> /// <returns><see langword="true" /> when <paramref name="type"/> is an instance of <see cref="IAsyncEnumerable{T}"/>, othwerise <see langword="false"/>.</returns> public bool TryGetReader(Type type, out Func <object, Task <ICollection> > reader) { if (!_asyncEnumerableConverters.TryGetValue(type, out reader)) { var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IAsyncEnumerable <>)); if (enumerableType is null) { // Not an IAsyncEnumerable<T>. Cache this result so we avoid reflection the next time we see this type. reader = null; _asyncEnumerableConverters.TryAdd(type, reader); } else { var enumeratedObjectType = enumerableType.GetGenericArguments()[0]; var converter = (Func <object, Task <ICollection> >)Converter .MakeGenericMethod(enumeratedObjectType) .CreateDelegate(typeof(Func <object, Task <ICollection> >), this); reader = converter; _asyncEnumerableConverters.TryAdd(type, reader); } } return(reader != null); }
/// <summary> /// Gets an <see cref="EnumerableWrapperProvider"/> for the provided context. /// </summary> /// <param name="context">The <see cref="WrapperProviderContext"/>.</param> /// <returns>An instance of <see cref="EnumerableWrapperProvider"/> if the declared type is /// an interface and implements <see cref="IEnumerable{T}"/>.</returns> public IWrapperProvider GetProvider([NotNull] WrapperProviderContext context) { if (context.IsSerialization) { // Example: IEnumerable<SerializableError> var declaredType = context.DeclaredType; var declaredTypeInfo = declaredType.GetTypeInfo(); // We only wrap interfaces types(ex: IEnumerable<T>, IQueryable<T>, IList<T> etc.) and not // concrete types like List<T>, Collection<T> which implement IEnumerable<T>. if (declaredType != null && declaredTypeInfo.IsInterface && declaredTypeInfo.IsGenericType) { var enumerableOfT = ClosedGenericMatcher.ExtractGenericInterface( declaredType, typeof(IEnumerable <>)); if (enumerableOfT != null) { var elementType = enumerableOfT.GenericTypeArguments[0]; var wrapperProviderContext = new WrapperProviderContext(elementType, context.IsSerialization); var elementWrapperProvider = _wrapperProviderFactories.GetWrapperProvider(wrapperProviderContext); return(new EnumerableWrapperProvider(enumerableOfT, elementWrapperProvider)); } } } return(null); }
/// <inheritdoc /> public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var modelType = context.Metadata.ModelType; var dictionaryType = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(IDictionary <,>)); if (dictionaryType != null) { var keyType = dictionaryType.GenericTypeArguments[0]; var keyBinder = context.CreateBinder(context.MetadataProvider.GetMetadataForType(keyType)); var valueType = dictionaryType.GenericTypeArguments[1]; var valueBinder = context.CreateBinder(context.MetadataProvider.GetMetadataForType(valueType)); var binderType = typeof(DictionaryModelBinder <,>).MakeGenericType(dictionaryType.GenericTypeArguments); var loggerFactory = context.Services.GetRequiredService <ILoggerFactory>(); return((IModelBinder)Activator.CreateInstance(binderType, keyBinder, valueBinder, loggerFactory)); } return(null); }
/// <summary> /// Initializes an instance of <see cref="EnumerableWrapperProvider"/>. /// </summary> /// <param name="sourceEnumerableOfT">Type of the original <see cref="IEnumerable{T}" /> /// that is being wrapped.</param> /// <param name="elementWrapperProvider">The <see cref="IWrapperProvider"/> for the element type. /// Can be null.</param> public EnumerableWrapperProvider( Type sourceEnumerableOfT, IWrapperProvider?elementWrapperProvider) { if (sourceEnumerableOfT == null) { throw new ArgumentNullException(nameof(sourceEnumerableOfT)); } var enumerableOfT = ClosedGenericMatcher.ExtractGenericInterface( sourceEnumerableOfT, typeof(IEnumerable <>)); if (!sourceEnumerableOfT.IsInterface || enumerableOfT == null) { throw new ArgumentException( Resources.FormatEnumerableWrapperProvider_InvalidSourceEnumerableOfT(typeof(IEnumerable <>).Name), nameof(sourceEnumerableOfT)); } _wrapperProvider = elementWrapperProvider; var declaredElementType = enumerableOfT.GenericTypeArguments[0]; var wrappedElementType = elementWrapperProvider?.WrappingType ?? declaredElementType; WrappingType = typeof(DelegatingEnumerable <,>).MakeGenericType(wrappedElementType, declaredElementType); _wrappingTypeConstructor = WrappingType.GetConstructor(new[] { sourceEnumerableOfT, typeof(IWrapperProvider) }) !; }
public static TryGetValueDelegate CreateInstance(Type targetType) { if (targetType == null) { throw new ArgumentNullException(nameof(targetType)); } TryGetValueDelegate result; // Cache delegates since properties of model types are re-evaluated numerous times. _tryGetValueDelegateCacheLock.EnterReadLock(); try { if (_tryGetValueDelegateCache.TryGetValue(targetType, out result)) { return(result); } } finally { _tryGetValueDelegateCacheLock.ExitReadLock(); } var dictionaryType = ClosedGenericMatcher.ExtractGenericInterface(targetType, typeof(IDictionary <,>)); // Just wrap a call to the underlying IDictionary<TKey, TValue>.TryGetValue() where string can be cast to // TKey. if (dictionaryType != null) { var typeArguments = dictionaryType.GenericTypeArguments; var keyType = typeArguments[0]; var returnType = typeArguments[1]; if (keyType.IsAssignableFrom(typeof(string))) { var implementationMethod = _strongTryGetValueImplInfo.MakeGenericMethod(keyType, returnType); result = (TryGetValueDelegate)implementationMethod.CreateDelegate(typeof(TryGetValueDelegate)); } } // Wrap a call to the underlying IDictionary.Item(). if (result == null && typeof(IDictionary).IsAssignableFrom(targetType)) { result = TryGetValueFromNonGenericDictionary; } _tryGetValueDelegateCacheLock.EnterWriteLock(); try { _tryGetValueDelegateCache[targetType] = result; } finally { _tryGetValueDelegateCacheLock.ExitWriteLock(); } return(result); }
/// <inheritdoc /> public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var modelType = context.Metadata.ModelType; // Arrays are handled by another binder. if (modelType.IsArray) { return(null); } var loggerFactory = context.Services.GetRequiredService <ILoggerFactory>(); var mvcOptions = context.Services.GetRequiredService <IOptions <MvcOptions> >().Value; // If the model type is ICollection<> then we can call its Add method, so we can always support it. var collectionType = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(ICollection <>)); if (collectionType != null) { var elementType = collectionType.GenericTypeArguments[0]; var elementBinder = context.CreateBinder(context.MetadataProvider.GetMetadataForType(elementType)); var binderType = typeof(CollectionModelBinder <>).MakeGenericType(collectionType.GenericTypeArguments); return((IModelBinder)Activator.CreateInstance( binderType, elementBinder, loggerFactory, mvcOptions.AllowValidatingTopLevelNodes)); } // If the model type is IEnumerable<> then we need to know if we can assign a List<> to it, since // that's what we would create. (The cases handled here are IEnumerable<>, IReadOnlyCollection<> and // IReadOnlyList<>). var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(IEnumerable <>)); if (enumerableType != null) { var listType = typeof(List <>).MakeGenericType(enumerableType.GenericTypeArguments); if (modelType.GetTypeInfo().IsAssignableFrom(listType.GetTypeInfo())) { var elementType = enumerableType.GenericTypeArguments[0]; var elementBinder = context.CreateBinder(context.MetadataProvider.GetMetadataForType(elementType)); var binderType = typeof(CollectionModelBinder <>).MakeGenericType(enumerableType.GenericTypeArguments); return((IModelBinder)Activator.CreateInstance( binderType, elementBinder, loggerFactory, mvcOptions.AllowValidatingTopLevelNodes)); } } return(null); }
/// <inheritdoc /> public ITypeInfo[] GetGenericDictionaryParameters() { return(ClosedGenericMatcher.ExtractGenericInterface( TypeInfo.AsType(), typeof(IDictionary <,>)) ?.GenericTypeArguments .Select(type => type.IsGenericParameter ? null : new RuntimeTypeInfo(type.GetTypeInfo())) .ToArray()); }
// Get the generic arguments for the binder, based on the model type. Or null if not compatible. private static Type[] GetGenericBinderTypeArgs(Type supportedInterfaceType, Type modelType) { Debug.Assert(supportedInterfaceType != null); Debug.Assert(modelType != null); var closedGenericInterface = ClosedGenericMatcher.ExtractGenericInterface(modelType, supportedInterfaceType); return(closedGenericInterface?.GenericTypeArguments); }
public void ExtractGenericInterface_MultipleDefinitionsOnSameType() { // Arrange var type = typeof(TwoIEnumerableImplementationsOnSameClass); // Act var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable <>)); // Sort Assert.Equal(typeof(IEnumerable <int>), result); }
public void ExtractGenericInterface_ReturnsExpectedType( Type queryType, Type interfaceType, Type expectedResult) { // Arrange & Act var result = ClosedGenericMatcher.ExtractGenericInterface(queryType, interfaceType); // Assert Assert.Equal(expectedResult, result); }
public static bool CanSerializeType(Type typeToSerialize, out string errorMessage) { typeToSerialize = typeToSerialize ?? throw new ArgumentNullException(nameof(typeToSerialize)); errorMessage = null; Type actualType = null; if (typeToSerialize.IsArray) { actualType = typeToSerialize.GetElementType(); } else if (typeToSerialize.GetTypeInfo().IsGenericType) { if (ClosedGenericMatcher.ExtractGenericInterface(typeToSerialize, typeof(IList <>)) != null) { var genericTypeArguments = typeToSerialize.GenericTypeArguments; Debug.Assert(genericTypeArguments.Length == 1, "IList<T> has one generic argument"); actualType = genericTypeArguments[0]; } else if (ClosedGenericMatcher.ExtractGenericInterface(typeToSerialize, typeof(IDictionary <,>)) != null) { var genericTypeArguments = typeToSerialize.GenericTypeArguments; Debug.Assert( genericTypeArguments.Length == 2, "IDictionary<TKey, TValue> has two generic arguments"); // The key must be of type string. if (genericTypeArguments[0] != typeof(string)) { errorMessage = Resources.FormatTempData_CannotSerializeDictionary( typeof(TempDataSerializer).FullName, genericTypeArguments[0], typeof(string).FullName); return(false); } else { actualType = genericTypeArguments[1]; } } } actualType = actualType ?? typeToSerialize; if (!IsSimpleType(actualType)) { errorMessage = Resources.FormatTempData_CannotSerializeType( typeof(TempDataSerializer).FullName, actualType); return(false); } return(true); }
private void ActivateProperties(object controller, Type containerType, Dictionary <string, object> properties) { var propertyHelpers = PropertyHelper.GetProperties(controller); foreach (var property in properties) { var propertyHelper = propertyHelpers.First(helper => string.Equals(helper.Name, property.Key, StringComparison.Ordinal)); var propertyType = propertyHelper.Property.PropertyType; var source = property.Value; if (propertyHelper.Property.CanWrite && propertyHelper.Property.SetMethod?.IsPublic == true) { // Handle settable property. Do not set the property if the type is a non-nullable type. if (source != null || TypeHelper.AllowsNullValue(propertyType)) { propertyHelper.SetValue(controller, source); } continue; } if (propertyType.IsArray) { // Do not attempt to copy values into an array because an array's length is immutable. This choice // is also consistent with MutableObjectModelBinder's handling of a read-only array property. continue; } var target = propertyHelper.GetValue(controller); if (source == null || target == null) { // Nothing to do when source or target is null. continue; } // Determine T if this is an ICollection<T> property. var collectionTypeArguments = ClosedGenericMatcher.ExtractGenericInterface( propertyType, typeof(ICollection <>)) ?.GenericTypeArguments; if (collectionTypeArguments == null) { // Not a collection model. continue; } // Handle a read-only collection property. var propertyAddRange = CallPropertyAddRangeOpenGenericMethod.MakeGenericMethod( collectionTypeArguments); propertyAddRange.Invoke(obj: null, parameters: new[] { target, source }); } }
private void InitializeTypeInformation() { Debug.Assert(ModelType != null); IsComplexType = !TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string)); IsNullableValueType = Nullable.GetUnderlyingType(ModelType) != null; IsReferenceOrNullableType = !ModelType.IsValueType || IsNullableValueType; UnderlyingOrModelType = Nullable.GetUnderlyingType(ModelType) ?? ModelType; var collectionType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(ICollection <>)); IsCollectionType = collectionType != null; var nullabilityContext = new NullabilityInfoContext(); var nullability = MetadataKind switch { ModelMetadataKind.Parameter => Identity.ParameterInfo != null?nullabilityContext.Create(Identity.ParameterInfo !) : null, ModelMetadataKind.Property => Identity.PropertyInfo != null?nullabilityContext.Create(Identity.PropertyInfo !) : null, _ => null }; NullabilityState = nullability?.ReadState ?? NullabilityState.Unknown; if (ModelType == typeof(string) || !typeof(IEnumerable).IsAssignableFrom(ModelType)) { // Do nothing, not Enumerable. } else if (ModelType.IsArray) { IsEnumerableType = true; ElementType = ModelType.GetElementType() !; } else { IsEnumerableType = true; var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(IEnumerable <>)); ElementType = enumerableType?.GenericTypeArguments[0] !; if (ElementType == null) { // ModelType implements IEnumerable but not IEnumerable<T>. ElementType = typeof(object); } Debug.Assert( ElementType != null, $"Unable to find element type for '{ModelType.FullName}' though IsEnumerableType is true."); } }
private static Type GetKeyValuePairBinder(Type modelType) { Debug.Assert(modelType != null); // Since KeyValuePair is a value type, ExtractGenericInterface() succeeds only on an exact match. var closedGenericType = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(KeyValuePair <,>)); if (closedGenericType != null) { return(typeof(KeyValuePairModelBinder <,>).MakeGenericType(modelType.GenericTypeArguments)); } return(null); }
/// <inheritdoc /> public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var modelType = context.Metadata.ModelType; // Arrays are handled by another binder. if (modelType.IsArray) { return(null); } // If the model type is ICollection<> then we can call its Add method, so we can always support it. var collectionType = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(ICollection <>)); if (collectionType != null) { var elementType = collectionType.GenericTypeArguments[0]; var elementBinder = context.CreateBinder(context.MetadataProvider.GetMetadataForType(elementType)); var binderType = typeof(CollectionModelBinder <>).MakeGenericType(collectionType.GenericTypeArguments); return((IModelBinder)Activator.CreateInstance(binderType, elementBinder)); } // If the model type is IEnumerable<> then we need to know if we can assign a List<> to it, since // that's what we would create. (The cases handled here are IEnumerable<>, IReadOnlyColection<> and // IReadOnlyList<>). // // We need to check IsReadOnly because we need to know if we can SET the property. var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(IEnumerable <>)); if (enumerableType != null && !context.Metadata.IsReadOnly) { var listType = typeof(List <>).MakeGenericType(enumerableType.GenericTypeArguments); if (modelType.GetTypeInfo().IsAssignableFrom(listType.GetTypeInfo())) { var elementType = enumerableType.GenericTypeArguments[0]; var elementBinder = context.CreateBinder(context.MetadataProvider.GetMetadataForType(elementType)); var binderType = typeof(CollectionModelBinder <>).MakeGenericType(enumerableType.GenericTypeArguments); return((IModelBinder)Activator.CreateInstance(binderType, elementBinder)); } } return(null); }
private void AddToProperty( ModelBindingContext bindingContext, ModelExplorer modelExplorer, PropertyInfo property, ModelBindingResult result) { var propertyExplorer = modelExplorer.GetExplorerForProperty(property.Name); var target = propertyExplorer.Model; var source = result.Model; if (target == null || source == null) { // Cannot copy to or from a null collection. return; } if (target == source) { // Added to the target collection in BindPropertiesAsync(). return; } // Determine T if this is an ICollection<T> property. No need for a T[] case because CanUpdateProperty() // ensures property is either settable or not an array. Underlying assumption is that CanUpdateProperty() // and SetProperty() are overridden together. var collectionTypeArguments = ClosedGenericMatcher.ExtractGenericInterface( propertyExplorer.ModelType, typeof(ICollection <>)) ?.GenericTypeArguments; if (collectionTypeArguments == null) { // Not a collection model. return; } var propertyAddRange = CallPropertyAddRangeOpenGenericMethod.MakeGenericMethod(collectionTypeArguments); try { propertyAddRange.Invoke(obj: null, parameters: new[] { target, source }); } catch (Exception exception) { AddModelError(exception, bindingContext, result); } }
internal void EnsureObjectCanBeSerialized(object item) { var itemType = item.GetType(); Type actualType = null; if (itemType.IsArray) { itemType = itemType.GetElementType(); } else if (itemType.GetTypeInfo().IsGenericType) { if (ClosedGenericMatcher.ExtractGenericInterface(itemType, typeof(IList <>)) != null) { var genericTypeArguments = itemType.GenericTypeArguments; Debug.Assert(genericTypeArguments.Length == 1, "IList<T> has one generic argument"); actualType = genericTypeArguments[0]; } else if (ClosedGenericMatcher.ExtractGenericInterface(itemType, typeof(IDictionary <,>)) != null) { var genericTypeArguments = itemType.GenericTypeArguments; Debug.Assert( genericTypeArguments.Length == 2, "IDictionary<TKey, TValue> has two generic arguments"); // Throw if the key type of the dictionary is not string. if (genericTypeArguments[0] != typeof(string)) { var message = Resources.FormatTempData_CannotSerializeDictionary( typeof(SessionStateTempDataProvider).FullName, genericTypeArguments[0]); throw new InvalidOperationException(message); } else { actualType = genericTypeArguments[1]; } } } actualType = actualType ?? itemType; if (!IsSimpleType(actualType)) { var underlyingType = Nullable.GetUnderlyingType(actualType) ?? actualType; var message = Resources.FormatTempData_CannotSerializeToSession( typeof(SessionStateTempDataProvider).FullName, underlyingType); throw new InvalidOperationException(message); } }
private void InitializeTypeInformation() { Debug.Assert(ModelType != null); IsComplexType = !TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string)); IsNullableValueType = Nullable.GetUnderlyingType(ModelType) != null; IsReferenceOrNullableType = !ModelType.GetTypeInfo().IsValueType || IsNullableValueType; UnderlyingOrModelType = Nullable.GetUnderlyingType(ModelType) ?? ModelType; var collectionType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(ICollection <>)); IsCollectionType = collectionType != null; if (ModelType == typeof(string) || !typeof(IEnumerable).IsAssignableFrom(ModelType)) { // Do nothing, not Enumerable. } else if (ModelType.IsArray) { IsEnumerableType = true; ElementType = ModelType.GetElementType(); } else { IsEnumerableType = true; var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(IEnumerable <>)); ElementType = enumerableType?.GenericTypeArguments[0]; if (ElementType == null && typeof(IEnumerable).IsAssignableFrom(ModelType)) { // ModelType implements IEnumerable but not IEnumerable<T>. ElementType = typeof(object); } Debug.Assert( ElementType != null, $"Unable to find element type for '{ModelType.FullName}' though IsEnumerableType is true."); } }
public IModelBinder?GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var modelType = context.Metadata.ModelType; // Arrays are handled by another binder. if (modelType.IsArray) { return(null); } // If the model type is ICollection<> then we can call its Add method, so we can always support it. var collectionType = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(ICollection <>)); if (collectionType != null) { return(CreateInstance(context, collectionType)); } // If the model type is IEnumerable<> then we need to know if we can assign a List<> to it, since // that's what we would create. (The cases handled here are IEnumerable<>, IReadOnlyCollection<> and // IReadOnlyList<>). var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(IEnumerable <>)); if (enumerableType != null) { var listType = typeof(List <>).MakeGenericType(enumerableType.GenericTypeArguments); if (modelType.IsAssignableFrom(listType)) { return(CreateInstance(context, listType)); } } return(null); }
public static string CollectionTemplate(IHtmlHelper htmlHelper) { var viewData = htmlHelper.ViewData; var model = viewData.Model; if (model == null) { return(string.Empty); } var collection = model as IEnumerable; if (collection == null) { // Only way we could reach here is if user passed templateName: "Collection" to an Editor() overload. throw new InvalidOperationException(Resources.FormatTemplates_TypeMustImplementIEnumerable( "Collection", model.GetType().FullName, typeof(IEnumerable).FullName)); } var typeInCollection = typeof(string); var genericEnumerableType = ClosedGenericMatcher.ExtractGenericInterface( collection.GetType(), typeof(IEnumerable <>)); if (genericEnumerableType != null) { typeInCollection = genericEnumerableType.GenericTypeArguments[0]; } var typeInCollectionIsNullableValueType = TypeHelper.IsNullableValueType(typeInCollection); var oldPrefix = viewData.TemplateInfo.HtmlFieldPrefix; try { viewData.TemplateInfo.HtmlFieldPrefix = string.Empty; var fieldNameBase = oldPrefix; var result = new StringBuilder(); var serviceProvider = htmlHelper.ViewContext.HttpContext.RequestServices; var metadataProvider = serviceProvider.GetRequiredService <IModelMetadataProvider>(); var viewEngine = serviceProvider.GetRequiredService <ICompositeViewEngine>(); var index = 0; foreach (var item in collection) { var itemType = typeInCollection; if (item != null && !typeInCollectionIsNullableValueType) { itemType = item.GetType(); } var modelExplorer = metadataProvider.GetModelExplorerForType(itemType, item); var fieldName = string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", fieldNameBase, index++); var templateBuilder = new TemplateBuilder( viewEngine, htmlHelper.ViewContext, htmlHelper.ViewData, modelExplorer, htmlFieldName: fieldName, templateName: null, readOnly: false, additionalViewData: null); var output = templateBuilder.Build(); result.Append(output); } return(result.ToString()); } finally { viewData.TemplateInfo.HtmlFieldPrefix = oldPrefix; } }
private static Type GetTaskInnerTypeOrNull(Type type) { var genericType = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(Task <>)); return(genericType?.GenericTypeArguments[0]); }
private void InitializeTypeInformation() { var typeInfo = ModelType.GetTypeInfo(); IsComplexType = !TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string)); IsNullableValueType = Nullable.GetUnderlyingType(ModelType) != null; IsReferenceType = !typeInfo.IsValueType; IsReferenceOrNullableType = !typeInfo.IsValueType || IsNullableValueType; UnderlyingOrModelType = Nullable.GetUnderlyingType(ModelType) ?? ModelType; IsFormFile = typeof(IFormFile).IsAssignableFrom(ModelType); IsSimpleType = IsSimple(ModelType); IsSystemObject = ModelType == typeof(object); var collectionType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(ICollection <>)); IsCollectionType = collectionType != null; if (ModelType == typeof(string) || !typeof(IEnumerable).IsAssignableFrom(ModelType)) { // Do nothing, not Enumerable. } else if (ModelType.IsArray) { IsEnumerableType = true; var elementType = ModelType.GetElementType(); PropertyInfo propertyInfo = ContainerType?.GetProperty(PropertyName); if (propertyInfo != null) { ElementType = new ProxyModelMetadata(propertyInfo, ProxyModelMetadataIdentity.ForProperty(elementType, propertyInfo.Name, ModelType), reflectedType: _reflectedType); } IsFormFile = typeof(IFormFile).IsAssignableFrom(elementType); } else { IsEnumerableType = true; var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(IEnumerable <>)); var elementType = enumerableType?.GenericTypeArguments[0]; if (elementType != null) { IsFormFile = typeof(IFormFile).IsAssignableFrom(elementType); } if (elementType == null) { // ModelType implements IEnumerable but not IEnumerable<T>. elementType = typeof(object); } PropertyInfo propertyInfo = ContainerType?.GetProperty(PropertyName); if (propertyInfo != null) { ElementType = new ProxyModelMetadata(propertyInfo, ProxyModelMetadataIdentity.ForProperty(elementType, propertyInfo.Name, ModelType), reflectedType: _reflectedType); } } if (IsNullableValueType) { var elementType = Nullable.GetUnderlyingType(ModelType); PropertyInfo pInfo = ContainerType?.GetProperty(PropertyName); if (pInfo != null) { ElementType = new ProxyModelMetadata(pInfo, ProxyModelMetadataIdentity.ForProperty(elementType, pInfo.Name, ModelType), reflectedType: _reflectedType); } } if (IsComplexType && IsReferenceOrNullableType && (!IsEnumerableType && !IsCollectionType) && ModelType.Name != typeof(object).Name) { PropertyInfo[] properties = ModelType.GetProperties(); List <ProxyModelMetadata> metadataList = new List <ProxyModelMetadata>(); foreach (var prop in properties) { if (_reflectedType == null) { _reflectedType = prop.ReflectedType; } if (_reflectedType == prop.PropertyType) { // self reference loop continue; } if (ModelType == prop.PropertyType) { // Parent child ref - self reference loop continue; } metadataList.Add(new ProxyModelMetadata(prop, ProxyModelMetadataIdentity.ForProperty(prop.PropertyType, prop.Name, ModelType), reflectedType: _reflectedType)); } Properties = metadataList; } }
private TagHelperAttributeDescriptor ToIndexerAttributeDescriptor( PropertyInfo property, HtmlAttributeNameAttribute attributeNameAttribute, Type parentType, ErrorSink errorSink, string defaultPrefix, out bool isInvalid) { isInvalid = false; var hasPublicSetter = property.SetMethod != null && property.SetMethod.IsPublic; var dictionaryTypeArguments = ClosedGenericMatcher.ExtractGenericInterface( property.PropertyType, typeof(IDictionary<,>)) ?.GenericTypeArguments .Select(type => type.IsGenericParameter ? null : type) .ToArray(); if (dictionaryTypeArguments?[0] != typeof(string)) { if (attributeNameAttribute?.DictionaryAttributePrefix != null) { // DictionaryAttributePrefix is not supported unless associated with an // IDictionary<string, TValue> property. isInvalid = true; errorSink.OnError( SourceLocation.Zero, Resources.FormatTagHelperDescriptorFactory_InvalidAttributePrefixNotNull( parentType.FullName, property.Name, nameof(HtmlAttributeNameAttribute), nameof(HtmlAttributeNameAttribute.DictionaryAttributePrefix), "IDictionary<string, TValue>"), length: 0); } else if (attributeNameAttribute != null && !hasPublicSetter) { // Associated an HtmlAttributeNameAttribute with a non-dictionary property that lacks a public // setter. isInvalid = true; errorSink.OnError( SourceLocation.Zero, Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameAttribute( parentType.FullName, property.Name, nameof(HtmlAttributeNameAttribute), "IDictionary<string, TValue>"), length: 0); } return null; } else if (!hasPublicSetter && attributeNameAttribute != null && !attributeNameAttribute.DictionaryAttributePrefixSet) { // Must set DictionaryAttributePrefix when using HtmlAttributeNameAttribute with a dictionary property // that lacks a public setter. isInvalid = true; errorSink.OnError( SourceLocation.Zero, Resources.FormatTagHelperDescriptorFactory_InvalidAttributePrefixNull( parentType.FullName, property.Name, nameof(HtmlAttributeNameAttribute), nameof(HtmlAttributeNameAttribute.DictionaryAttributePrefix), "IDictionary<string, TValue>"), length: 0); return null; } // Potential prefix case. Use default prefix (based on name)? var useDefault = attributeNameAttribute == null || !attributeNameAttribute.DictionaryAttributePrefixSet; var prefix = useDefault ? defaultPrefix : attributeNameAttribute.DictionaryAttributePrefix; if (prefix == null) { // DictionaryAttributePrefix explicitly set to null. Ignore. return null; } return ToAttributeDescriptor( property, attributeName: prefix, typeName: dictionaryTypeArguments[1].FullName, isIndexer: true, isStringProperty: typeof(string) == dictionaryTypeArguments[1]); }