protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
    {
        PropertyBinderAttribute attribute = propertyDescriptor.Attributes
                                            .OfType <PropertyBinderAttribute>()
                                            .SingleOrDefault();

        if (attribute == null)
        {
            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        }
        else
        {
            propertyDescriptor.SetValue(bindingContext.Model, attribute.GetBinder().BindProperty(controllerContext, bindingContext, propertyDescriptor));
        }
    }
Exemplo n.º 2
0
 IModelBinder CreateBinder(PropertyBinderAttribute propertyBinderAttribute)
 {
     return((IModelBinder)DependencyResolver.Current.GetService(propertyBinderAttribute.BinderType));
 }
Exemplo n.º 3
0
        protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
        {
            // Check if the property has the PropertyBinderAttribute, meaning it's specifying a different binder to use.
            PropertyBinderAttribute propertyBinderAttribute = TryFindPropertyBinderAttribute(propertyDescriptor);

            if (propertyBinderAttribute != null)
            {
                string keyName = string.Format("{0}{2}{1}", bindingContext.ModelName, propertyDescriptor.Name, string.IsNullOrWhiteSpace(bindingContext.ModelName) ? "" : ".");

                //Is this a collection of other things?
                if (propertyDescriptor.PropertyType.IsArray || (!typeof(string).Equals(propertyDescriptor.PropertyType) && typeof(IEnumerable).IsAssignableFrom(propertyDescriptor.PropertyType)))
                {
                    FormValueProvider formValueProvider = (FormValueProvider)((ValueProviderCollection)bindingContext.ValueProvider).FirstOrDefault(vp => vp.GetType() == typeof(FormValueProvider));

                    //We have to get the keys from the valid provider that match the pattern razor is feeding us "type.type.type[#].type[#] potentially"
                    IList <string> keys = new List <string>();

                    int i = 0;
                    foreach (KeyValuePair <string, string> key in formValueProvider.GetKeysFromPrefix(keyName))
                    {
                        string oldKey     = key.Value;
                        string reKeyedKey = string.Format("{1}[{0}]{2}", i, oldKey.Substring(0, oldKey.IndexOf('[')), oldKey.Substring(oldKey.IndexOf(']') + 1));

                        keys.Add(reKeyedKey);

                        i++;
                    }

                    IEnumerable <string> values = keys.Select(kvp => bindingContext.ValueProvider.GetValue(kvp).AttemptedValue);

                    propertyDescriptor.SetValue(bindingContext.Model, propertyBinderAttribute.Convert(values));
                }
                else
                {
                    ValueProviderResult value = bindingContext.ValueProvider.GetValue(keyName);

                    //Bound values *on* the view model tend to double their names due to stupidity
                    if (value == null)
                    {
                        value = bindingContext.ValueProvider.GetValue(string.Format("{0}.{0}", propertyDescriptor.Name));
                    }

                    if (value == null)
                    {
                        value = bindingContext.ValueProvider.GetValue(string.Format("{0}.{1}.{1}", bindingContext.ModelName, propertyDescriptor.Name));
                    }

                    if (value != null)
                    {
                        //If we got the value we're good just set it
                        propertyDescriptor.SetValue(bindingContext.Model, propertyBinderAttribute.Convert(value.AttemptedValue));
                    }
                    else if ((propertyDescriptor.PropertyType.IsInterface || propertyDescriptor.PropertyType.IsClass) && !typeof(string).Equals(propertyDescriptor.PropertyType))
                    {
                        object[] props = new object[propertyDescriptor.GetChildProperties().Count];
                        int      i     = 0;

                        //Do we have a class or interface? We want top parse ALL the submitted values in the post and try to fill that one class object up with its props
                        foreach (PropertyDescriptor prop in propertyDescriptor.GetChildProperties())
                        {
                            PropertyBinderAttribute childBinder = TryFindPropertyBinderAttribute(prop);
                            string childKeyName = string.Format("{0}.{1}", keyName, prop.Name);

                            //Collection shenanigans, we need to create the right collection and put it back on the post value collection so a later call to this can fill it correctly
                            if (prop.PropertyType.IsArray || (!typeof(string).Equals(prop.PropertyType) && typeof(IEnumerable).IsAssignableFrom(prop.PropertyType)))
                            {
                                FormValueProvider formValueProvider = (FormValueProvider)((ValueProviderCollection)bindingContext.ValueProvider).FirstOrDefault(vp => vp.GetType() == typeof(FormValueProvider));

                                //We have to get the keys from the valid provider that match the pattern razor is feeding us "type.type.type[#].type[#] potentially"
                                IList <string> keys = new List <string>();

                                int index = 0;
                                foreach (KeyValuePair <string, string> key in formValueProvider.GetKeysFromPrefix(childKeyName))
                                {
                                    string oldKey     = key.Value;
                                    string reKeyedKey = string.Format("{1}[{0}]{2}", index, oldKey.Substring(0, oldKey.IndexOf('[')), oldKey.Substring(oldKey.IndexOf(']') + 1));

                                    keys.Add(reKeyedKey);

                                    index++;
                                }

                                if (keys.Count > 0)
                                {
                                    IEnumerable <string> values = keys.Select(kvp => bindingContext.ValueProvider.GetValue(kvp).AttemptedValue);

                                    if (childBinder != null)
                                    {
                                        props[i] = childBinder.Convert(values);
                                    }
                                    else
                                    {
                                        props[i] = values;
                                    }
                                }
                            }
                            else
                            {
                                //I guess we didnt have a class so just try and use the modelbinder to convert the value correctly
                                ValueProviderResult childValue = bindingContext.ValueProvider.GetValue(childKeyName);

                                if (childValue != null)
                                {
                                    if (childBinder != null)
                                    {
                                        props[i] = childBinder.Convert(childValue);
                                    }
                                    else
                                    {
                                        props[i] = childValue.AttemptedValue;
                                    }
                                }
                            }

                            i++;
                        }

                        //Did we actually find the properties for the class?
                        if (!props.Any(prop => prop == null))
                        {
                            //Interface shenanigans again
                            if (propertyDescriptor.PropertyType.IsInterface)
                            {
                                Type type = null;

                                if (propertyDescriptor.PropertyType == typeof(ILocationData))
                                {
                                    type = typeof(RoomTemplate);
                                }
                                else
                                {
                                    type = typeof(EntityPartial).Assembly.GetTypes().SingleOrDefault(x => !x.IsAbstract && x.GetInterfaces().Contains(propertyDescriptor.PropertyType));

                                    if (type == null)
                                    {
                                        type = typeof(SensoryEvent).Assembly.GetTypes().SingleOrDefault(x => !x.IsAbstract && x.GetInterfaces().Contains(propertyDescriptor.PropertyType));
                                    }
                                }

                                if (type == null)
                                {
                                    throw new Exception("Invalid Binding Interface");
                                }

                                object concreteInstance = Activator.CreateInstance(type, props);

                                if (concreteInstance != null)
                                {
                                    propertyDescriptor.SetValue(bindingContext.Model, concreteInstance);
                                }
                            }
                            else
                            {
                                object newItem = Activator.CreateInstance(propertyDescriptor.PropertyType, props);

                                if (newItem != null)
                                {
                                    propertyDescriptor.SetValue(bindingContext.Model, newItem);
                                }
                            }
                        }
                        else
                        {
                            //I guess we didnt actually want the entire class, just the ID so we can find it in the cache (eg. we had a dropdown of "select a class" as a prop)
                            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
                        }
                    }
                }

                return;
            }
            else if (propertyDescriptor.PropertyType.IsArray ||
                     (!typeof(string).Equals(propertyDescriptor.PropertyType) && typeof(IEnumerable).IsAssignableFrom(propertyDescriptor.PropertyType)))
            {
                string keyName = string.Format("{0}{2}{1}", bindingContext.ModelName, propertyDescriptor.Name, string.IsNullOrWhiteSpace(bindingContext.ModelName) ? "" : ".");

                ValueProviderResult value = bindingContext.ValueProvider.GetValue(keyName);

                //Bound values *on* the view model tend to double their names due to stupidity
                if (value == null)
                {
                    value = bindingContext.ValueProvider.GetValue(string.Format("{0}.{0}", propertyDescriptor.Name));
                }

                if (value == null)
                {
                    value = bindingContext.ValueProvider.GetValue(string.Format("{0}.{1}.{1}", bindingContext.ModelName, propertyDescriptor.Name));
                }

                if (value != null)
                {
                    //If we got the value we're good just set it
                    if (propertyBinderAttribute == null)
                    {
                        base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
                    }
                    else
                    {
                        propertyDescriptor.SetValue(bindingContext.Model, propertyBinderAttribute.Convert(value.AttemptedValue));
                    }

                    return;
                }
                else if (!string.IsNullOrWhiteSpace(bindingContext.ModelName))
                {
                    Type containedType = null;
                    Type itemType      = null;

                    if (propertyDescriptor.PropertyType.IsArray)
                    {
                        itemType      = propertyDescriptor.PropertyType.GetElementType();
                        containedType = itemType;
                    }
                    else
                    {
                        itemType      = propertyDescriptor.PropertyType.GetGenericArguments().First();
                        containedType = itemType;
                    }

                    if (containedType.IsInterface)
                    {
                        Type type = null;

                        if (containedType == typeof(ILocationData))
                        {
                            type = typeof(RoomTemplate);
                        }
                        else
                        {
                            type = typeof(EntityPartial).Assembly.GetTypes().SingleOrDefault(x => !x.IsAbstract && x.GetInterfaces().Contains(containedType));

                            if (type == null)
                            {
                                type = typeof(SensoryEvent).Assembly.GetTypes().SingleOrDefault(x => !x.IsAbstract && x.GetInterfaces().Contains(containedType));
                            }
                        }

                        containedType = type ?? throw new Exception("Invalid Binding Interface");
                    }

                    if (containedType.IsClass && !typeof(string).Equals(containedType))
                    {
                        System.Reflection.PropertyInfo[] properties = containedType.GetProperties();
                        FormValueProvider formValueProvider         = (FormValueProvider)((ValueProviderCollection)bindingContext.ValueProvider).FirstOrDefault(vp => vp.GetType() == typeof(FormValueProvider));

                        //HashSet<object> valueArray = new HashSet<object>();
                        dynamic valueArray = Activator.CreateInstance(propertyDescriptor.PropertyType, false);
                        foreach (KeyValuePair <string, string> baseKey in formValueProvider.GetKeysFromPrefix(keyName))
                        {
                            int     propIterator = 0;
                            dynamic newItem      = Activator.CreateInstance(containedType, false);

                            //Do we have a class or interface? We want top parse ALL the submitted values in the post and try to fill that one class object up with its props
                            foreach (System.Reflection.PropertyInfo prop in properties)
                            {
                                Type propType = prop.PropertyType;
                                PropertyBinderAttribute propertyBinder = (PropertyBinderAttribute)prop.GetCustomAttributes(typeof(PropertyBinderAttribute), true).FirstOrDefault();

                                string childKeyName = string.Format("{0}.{1}", baseKey.Value, prop.Name);

                                //Collection shenanigans, we need to create the right collection and put it back on the post value collection so a later call to this can fill it correctly
                                if (propType.IsArray || (!typeof(string).Equals(propType) && typeof(IEnumerable).IsAssignableFrom(propType)))
                                {
                                    //We have to get the keys from the valid provider that match the pattern razor is feeding us "type.type.type[#].type[#] potentially"
                                    IList <string> keys = new List <string>();

                                    int index = 0;
                                    foreach (KeyValuePair <string, string> key in formValueProvider.GetKeysFromPrefix(childKeyName))
                                    {
                                        string oldKey     = key.Value;
                                        string reKeyedKey = string.Format("{1}[{0}]{2}", index, oldKey.Substring(0, oldKey.LastIndexOf('[')), oldKey.Substring(oldKey.LastIndexOf(']') + 1));

                                        keys.Add(reKeyedKey);

                                        index++;
                                    }

                                    Type innerContainedType = null;
                                    Type innerItemType      = null;

                                    if (propType.IsArray)
                                    {
                                        innerItemType      = propType.GetElementType();
                                        innerContainedType = innerItemType;
                                    }
                                    else
                                    {
                                        innerItemType      = propType.GetGenericArguments().First();
                                        innerContainedType = innerItemType;
                                    }

                                    if (innerContainedType.IsInterface)
                                    {
                                        Type type = null;

                                        if (innerContainedType == typeof(ILocationData))
                                        {
                                            type = typeof(RoomTemplate);
                                        }
                                        else
                                        {
                                            type = typeof(EntityPartial).Assembly.GetTypes().SingleOrDefault(x => !x.IsAbstract && x.GetInterfaces().Contains(innerContainedType));

                                            if (type == null)
                                            {
                                                type = typeof(SensoryEvent).Assembly.GetTypes().SingleOrDefault(x => !x.IsAbstract && x.GetInterfaces().Contains(innerContainedType));
                                            }
                                        }

                                        innerContainedType = type ?? throw new Exception("Invalid Binding Interface");
                                    }

                                    if (keys.Count > 0)
                                    {
                                        if (innerContainedType.IsClass && !typeof(string).Equals(innerContainedType))
                                        {
                                            System.Reflection.PropertyInfo[] innerProperties = innerContainedType.GetProperties();
                                            dynamic innerValueArray = Activator.CreateInstance(propType, false);

                                            foreach (string innerBaseKey in keys)
                                            {
                                                dynamic innerNewItem = Activator.CreateInstance(innerContainedType, false);

                                                //Do we have a class or interface? We want top parse ALL the submitted values in the post and try to fill that one class object up with its props
                                                foreach (System.Reflection.PropertyInfo innerProp in innerProperties)
                                                {
                                                    Type innerPropType = innerProp.PropertyType;
                                                    PropertyBinderAttribute innerPropertyBinder = (PropertyBinderAttribute)innerProp.GetCustomAttributes(typeof(PropertyBinderAttribute), true).FirstOrDefault();

                                                    string innerChildKeyName = string.Format("{0}.{1}", innerBaseKey, innerProp.Name);

                                                    //Collection shenanigans, we need to create the right collection and put it back on the post value collection so a later call to this can fill it correctly
                                                    if (innerProp.PropertyType.IsArray || (!typeof(string).Equals(innerProp.PropertyType) && typeof(IEnumerable).IsAssignableFrom(innerProp.PropertyType)))
                                                    {
                                                        //We have to get the keys from the valid provider that match the pattern razor is feeding us "type.type.type[#].type[#] potentially"
                                                        IList <string> innerKeys = new List <string>();

                                                        int innerIndex = 0;
                                                        foreach (KeyValuePair <string, string> key in formValueProvider.GetKeysFromPrefix(innerChildKeyName))
                                                        {
                                                            string oldKey     = key.Value;
                                                            string reKeyedKey = string.Format("{1}[{0}]{2}", innerIndex, oldKey.Substring(0, oldKey.IndexOf('[')), oldKey.Substring(oldKey.IndexOf(']') + 1));

                                                            innerKeys.Add(reKeyedKey);

                                                            innerIndex++;
                                                        }

                                                        if (innerKeys.Count > 0)
                                                        {
                                                            IList <object> values = new List <object>();
                                                            if (propertyBinder != null)
                                                            {
                                                                foreach (string key in innerKeys)
                                                                {
                                                                    values.Add(propertyBinder.Convert(bindingContext.ValueProvider.GetValue(key)));
                                                                }
                                                            }
                                                            else
                                                            {
                                                                foreach (string key in innerKeys)
                                                                {
                                                                    values.Add(bindingContext.ValueProvider.GetValue(key).AttemptedValue);
                                                                }
                                                            }

                                                            innerProp.SetValue(innerNewItem, values);
                                                        }
                                                    }
                                                    else
                                                    {
                                                        //I guess we didnt have a class so just try and use the modelbinder to convert the value correctly
                                                        ValueProviderResult childValue = bindingContext.ValueProvider.GetValue(innerChildKeyName);

                                                        if (childValue == null)
                                                        {
                                                            childValue = bindingContext.ValueProvider.GetValue(string.Format("{0}.{0}", innerProp.Name));
                                                        }

                                                        if (childValue == null)
                                                        {
                                                            childValue = bindingContext.ValueProvider.GetValue(string.Format("{0}.{1}.{1}", innerBaseKey, innerProp.Name));
                                                        }

                                                        if (childValue != null)
                                                        {
                                                            if (propertyBinder != null)
                                                            {
                                                                innerProp.SetValue(innerNewItem, propertyBinder.Convert(childValue.AttemptedValue));
                                                            }
                                                            else
                                                            {
                                                                innerProp.SetValue(innerNewItem, childValue.ConvertTo(innerProp.PropertyType));
                                                            }
                                                        }
                                                    }
                                                }

                                                if (innerNewItem != null)
                                                {
                                                    innerValueArray.Add(innerNewItem);
                                                }
                                            }

                                            prop.SetValue(newItem, innerValueArray);
                                        }
                                        else
                                        {
                                            IList <object> values = new List <object>();
                                            if (propertyBinder != null)
                                            {
                                                foreach (string key in keys)
                                                {
                                                    values.Add(propertyBinder.Convert(bindingContext.ValueProvider.GetValue(key)));
                                                }
                                            }
                                            else
                                            {
                                                foreach (string key in keys)
                                                {
                                                    values.Add(bindingContext.ValueProvider.GetValue(key).AttemptedValue);
                                                }
                                            }

                                            prop.SetValue(newItem, values);
                                        }
                                    }
                                }
                                else
                                {
                                    //I guess we didnt have a class so just try and use the modelbinder to convert the value correctly
                                    ValueProviderResult childValue = bindingContext.ValueProvider.GetValue(childKeyName);

                                    if (childValue == null)
                                    {
                                        childValue = bindingContext.ValueProvider.GetValue(string.Format("{0}.{0}", prop.Name));
                                    }

                                    if (childValue == null)
                                    {
                                        childValue = bindingContext.ValueProvider.GetValue(string.Format("{0}.{1}.{1}", baseKey.Value, prop.Name));
                                    }

                                    if (childValue != null)
                                    {
                                        if (propertyBinder != null)
                                        {
                                            prop.SetValue(newItem, propertyBinder.Convert(childValue.AttemptedValue));
                                        }
                                        else
                                        {
                                            prop.SetValue(newItem, childValue.ConvertTo(prop.PropertyType));
                                        }
                                    }
                                }

                                propIterator++;
                            }

                            if (newItem != null)
                            {
                                if (itemType != containedType)
                                {
                                    valueArray.Add(newItem);
                                }
                                else
                                {
                                    valueArray.Add(newItem);
                                }
                            }
                        }

                        propertyDescriptor.SetValue(bindingContext.Model, valueArray);
                        return;
                    }
                }
            }

            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        }
Exemplo n.º 4
0
        PropertyBinderAttribute TryFindPropertyBinderAttribute(PropertyDescriptor propertyDescriptor)
        {
            PropertyBinderAttribute binder = propertyDescriptor.Attributes.OfType <PropertyBinderAttribute>().FirstOrDefault();

            if (binder == null && propertyDescriptor.ComponentType.IsInterface && !propertyDescriptor.PropertyType.IsValueType)
            {
                Type componentType = propertyDescriptor.ComponentType;

                //Convert the interface to the concrete class by finding a concrete class that impls this interface
                if (!componentType.IsGenericType)
                {
                    Type type = null;

                    if (componentType == typeof(ILocationData))
                    {
                        if (componentType.Name.Contains("Zone"))
                        {
                            type = typeof(ZoneTemplate);
                        }
                        else if (componentType.Name.Contains("Room"))
                        {
                            type = typeof(RoomTemplate);
                        }
                    }
                    else
                    {
                        type = typeof(EntityPartial).Assembly.GetTypes().SingleOrDefault(x => !x.IsAbstract && x.GetInterfaces().Contains(componentType));

                        if (type == null)
                        {
                            type = typeof(SensoryEvent).Assembly.GetTypes().SingleOrDefault(x => !x.IsAbstract && x.GetInterfaces().Contains(componentType));
                        }
                    }

                    if (type == null)
                    {
                        return(null);
                    }

                    System.Reflection.PropertyInfo[] typeProps = type.GetProperties();
                    System.Reflection.PropertyInfo   myProp    = typeProps.FirstOrDefault(prop => prop.Name == propertyDescriptor.Name && prop.PropertyType == propertyDescriptor.PropertyType);

                    return((PropertyBinderAttribute)myProp.GetCustomAttributes(typeof(PropertyBinderAttribute), true).FirstOrDefault());
                }
                else
                {
                    //Our interface involves generics so go find the concrete class by type name match so we can build it out using the correct type for the generic parameter
                    string genericName = componentType.Name.Substring(1);
                    Type   type        = typeof(EntityPartial).Assembly.GetTypes().SingleOrDefault(x => !x.IsAbstract && x.IsGenericType && x.Name.Equals(genericName));

                    if (type == null)
                    {
                        return(null);
                    }

                    Type genericType = type.MakeGenericType(componentType.GenericTypeArguments);

                    System.Reflection.PropertyInfo[] typeProps = genericType.GetProperties();
                    System.Reflection.PropertyInfo   myProp    = typeProps.FirstOrDefault(prop => prop.Name == propertyDescriptor.Name && prop.PropertyType == propertyDescriptor.PropertyType);

                    return(myProp.CustomAttributes.OfType <PropertyBinderAttribute>().FirstOrDefault());
                }
            }

            return(binder);
        }