protected (ClassViewModel ServedType, ClassViewModel DeclaredFor) GetStrategyTypes( ModelBindingContext bindingContext, Type strategyInterface ) { // Grab the type information about what we need to inject. var parameterTypeViewModel = new ReflectionTypeViewModel(bindingContext.ModelType); // Figure out what type is satisfying the generic parameter of our strategy interface. // This is the type that our datasource/Behaviors needs to serve. var servedType = parameterTypeViewModel.GenericArgumentsFor(strategyInterface).Single().ClassViewModel; // Examine the parameter for any attributes. var parameterDescriptor = bindingContext.ActionContext.ActionDescriptor.Parameters .Single(p => p.Name == bindingContext.FieldName) as Microsoft.AspNetCore.Mvc.Controllers.ControllerParameterDescriptor; var parameterInfo = parameterDescriptor.ParameterInfo; var declaredForAttribute = parameterInfo.GetAttribute <DeclaredForAttribute>(); // See if there is an override for the type that we want to find declared data sources for. ClassViewModel declaredFor = servedType; if (declaredForAttribute != null) { declaredFor = ReflectionRepository.Global.GetClassViewModel(declaredForAttribute.DeclaredFor); } return(servedType, declaredFor); }
public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var typeViewModel = new ReflectionTypeViewModel(context.Metadata.ModelType); if (!typeViewModel.IsA(typeof(IBehaviors <>))) { return(null); } return(new BinderTypeModelBinder(typeof(BehaviorsModelBinder))); }
private static void Configure <T>(ClassMap map) { map.ReferenceMaps.Clear(); //http://stackoverflow.com/questions/1571022/how-would-i-know-if-a-property-is-a-generic-collection var membersToRemove = map.MemberMaps .Where(p => { // Remove non-properties. Coalesce only exposes properties. if (p.Data.Member.MemberType != System.Reflection.MemberTypes.Property) { return(true); } var memberType = new ReflectionTypeViewModel(p.Data.Member.MemberType()); return(memberType.IsA(typeof(ICollection <>))); }) .ToArray(); foreach (var pm in membersToRemove) { map.MemberMaps.Remove(pm); } }
private void SetupProps() { if (ViewModelType == typeof(ReflectionClassViewModel)) { ClassViewModel = ReflectionRepositoryFactory.Reflection.GetClassViewModel(TargetType); } else if (ViewModelType == typeof(SymbolClassViewModel)) { var fqn = new ReflectionTypeViewModel(TargetType).VerboseFullyQualifiedName; var symbolFormat = SymbolTypeViewModel.VerboseDisplayFormat; var locatedSymbol = ReflectionRepositoryFactory.Symbols .Where(symbol => symbol.ToDisplayString(symbolFormat) == fqn) .FirstOrDefault(); if (locatedSymbol == null) { throw new ArgumentException($"Class {TargetType} ({fqn}) not found in any C# embedded resources."); } ClassViewModel = ReflectionRepositoryFactory.Symbol.GetClassViewModel(locatedSymbol); } }
public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } // Specify a default argument name if none is set by ModelBinderAttribute // This is the name of the query parameter on the URL. if (string.IsNullOrEmpty(bindingContext.BinderModelName)) { bindingContext.BinderModelName = bindingContext.ModelName; } var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.BinderModelName); // This is the name of the dataSource that has been requested. var requestedDataSource = valueProviderResult.FirstValue; var(servedType, declaredFor) = GetStrategyTypes(bindingContext, typeof(IDataSource <>)); object dataSource; try { dataSource = dataSourceFactory.GetDataSource(servedType, declaredFor, requestedDataSource); } catch (DataSourceNotFoundException ex) { // A data-source that doesn't exist was requested. Add a binding error and quit. // The ApiController's IActionFilter (or individual actions) are responsible for handling the response for this error condition. bindingContext.ModelState.TryAddModelError(bindingContext.BinderModelName, ex.Message); return; } // We now have an IDataSource<> instance. Get its actual type so we can reflect on it. var dataSourceType = dataSource.GetType(); // From our concrete dataSource, figure out which properties on it are injectable parameters. var desiredPropertyViewModels = new ReflectionTypeViewModel(dataSourceType).ClassViewModel.DataSourceParameters; // Get the ASP.NET MVC metadata objects for these properties. var desiredPropertiesMetadata = desiredPropertyViewModels .Select(propViewModel => bindingContext.ModelMetadata.GetMetadataForProperty(dataSourceType, propViewModel.Name)) .ToList(); // Tell the validation stage that it should only perform validation // on the specific properties which we are binding to (and not ALL properties on the dataSource). bindingContext.ValidationState[dataSource] = new ValidationStateEntry() { Strategy = new SelectivePropertyComplexObjectValidationStrategy(desiredPropertiesMetadata) }; // Hijack ComplexTypeModelBinder to do our binding for us on the properties we care about. var childBinder = new ComplexTypeModelBinder(desiredPropertiesMetadata.ToDictionary( property => property, property => modelBinderFactory.CreateBinder(new ModelBinderFactoryContext { BindingInfo = new BindingInfo() { BinderModelName = property.BinderModelName, BinderType = property.BinderType, BindingSource = property.BindingSource, PropertyFilterProvider = property.PropertyFilterProvider, }, Metadata = property, CacheToken = property, }) ), new LoggerFactory()); // Enter a nested scope for binding the properties on our dataSource // (we're now 1 level deep instead of 0 levels deep). using (bindingContext.EnterNestedScope( bindingContext.ModelMetadata.GetMetadataForType(dataSourceType), bindingContext.FieldName, bindingContext.ModelName, dataSource)) { bindingContext.PropertyFilter = p => desiredPropertiesMetadata.Contains(p); // We call the private method "BindModelCoreAsync" here because // "BindModelAsync" performs a check to see if we should bother instantiating the root model (our dataSource). // We already instantiated our dataSource, so this check is meaningless. The consequence of this frivolous check is that // it causeses validation of client parameter properties // to not occurr if the client didn't provide any values for those parameters. // The alternative to do this would be to make a full copy of ComplexTypeModelBinder.cs and // change out the desired pieces. await(childBinder .GetType() .GetMethod("BindModelCoreAsync", BindingFlags.NonPublic | BindingFlags.Instance) .Invoke(childBinder, new[] { bindingContext }) as Task); // await childBinder.BindModelAsync(bindingContext); } // Everything worked out; we have a dataSource! // Hand back our resulting object, and we're done. bindingContext.Result = ModelBindingResult.Success(dataSource); }