使用反射的通用模型活页夹

时间:2012-10-17 14:08:14

标签: c# asp.net-mvc reflection

我正在尝试构建一个通用模型绑定器,它使用反射将属性分配给从BindingContext检索的指定类型的对象。所以像这样:

public class ModelBinder : IModelBinder
{
    public object BindModel<T, K> (ControllerContext controllerContext, ModelBindingContext bindingContext)
        where T : class, new()
        where K : class
    {
        Type ObjectType = typeof(T);
        Type InterfaceType = typeof(K);
        T obj = new T();

        foreach (var Property in ObjectType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))
        {
            Type PropertyType = Property.PropertyType;

            // checks if property is a custom data object
            if (!(PropertyType.GetGenericArguments().Count() > 0 && PropertyType.GetGenericArguments()[0].GetInterfaces().Contains(InterfaceType)))
            {
                Property.SetValue(obj, bindingContext.ValueProvider.GetValue(Property.Name), null);   
            }
        }

        return obj;
    }

显然这不起作用,因为它没有正确实现IModelBinder接口。这样的事情可能吗?

编辑:

为了扩展我为什么这样做,我们的对象使用了很多不同的自定义对象。例如,Class对象:

public class Class : ModelBase<Class>
{
    public Class () { }

    public virtual string ClassDescription { get; set; }
    public virtual string ClassName { get; set; }
    public virtual LookUp ClassType { get; set; }
    public virtual double Credits { get; set; }
    public virtual bool Documents { get; set; }
    public virtual LookUp PracticeArea { get; set; }
}

使用LookUp类:

public class LookUp : ModelBase<LookUp>
{
    public LookUp () { }

    public virtual string DisplayName { get; set; }
    public virtual LookUpType Type { get; set; }
}

下拉列表用于LookUps和其他对象,因此我的Class / Create自定义模型绑定器将执行以下操作:

LookUp ClassType = LookUp.Load(long.Parse(bindingContext.ValueProvider.GetValue("ClassType").AttemptedValue))

我不知道如何使用DefaultModelBinder处理这样的事情。

1 个答案:

答案 0 :(得分:1)

所以我想出了一个有效的(现在)解决方案。目前它不是很通用,但它适用于我的目的。

public class ModelBinder : DefaultModelBinder
{
    public override object BindModel (ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var ModelType = bindingContext.ModelType;
        var Instance = Activator.CreateInstance(ModelType);
        var Form = bindingContext.ValueProvider;

        foreach (var Property in ModelType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))
        {
            Type PropertyType = Property.PropertyType;

            // I'm using an ORM so this checks whether or not the property is a
            // reference to another object
            if (!(PropertyType.GetGenericArguments().Count() > 0 ))
            {
                // This is the not so generic part.  It really just checks whether or 
                // not it is a custom object.  Also the .Load() method is specific
                // to the ORM
                if (PropertyType.FullName.StartsWith("Objects.Models"))
                {
                    var Load = PropertyType.GetMethod("Load", BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Static, null, new Type[] { typeof(long) }, null);
                    var Value = Load.Invoke(new object(), new object[] { long.Parse(Form.GetValue(Property.Name + ".ID").AttemptedValue) });
                    Property.SetValue(Instance, Value, null);
                }
                // checkboxes are weird and require a special case
                else if (PropertyType.Equals(typeof(bool)))
                {
                    if (Form.GetValue(Property.Name) == null)
                    {
                        Property.SetValue(Instance, false, null);
                    }
                    else if (Form.GetValue(Property.Name).Equals("on"))
                    {
                        Property.SetValue(Instance, true, null);
                    }
                }
                else
                {
                    Property.SetValue(Instance, Convert.ChangeType(bindingContext.ValueProvider.GetValue(Property.Name).AttemptedValue, PropertyType), null);
                }
            }
        }

        return Instance;
    }
}