1

我在 ASP.NET MVC 中遇到了一个奇怪的问题,当传递一个formCollection时,对象没有被UpdateModel更新。当通过反射创建要更新的对象时, UpdateModel似乎无法正常工作。

场景:我有一个应用程序,它有大约 50 个查找表——每个查找表都包含完全相同的模式,包括 id、title、description、isactive 和 createdon 等典型字段。我不想构建 50 个视图,而是想要一个可以显示所有查找表中的数据的视图。我创建了一个名为 IReferenceEntity 的接口,并在代表我的查找表的每个 POCO 中实现了它。

使用此界面,我可以轻松地使用查找表中的记录填充视图。(我通过以下方式将项目传递给视图。)

System.Web.Mvc.ViewPage<MyNamespece.IReferenceEntity> 

从数据库到视图,每件事都完美无缺。

但是,当我尝试在帖子上更新模型时,我遇到了一些问题。

如果我像下面这样显式声明一个对象引用,那么每件事都会完美运行,并且我的对象的值会使用我的表单中的值进行更新。因此,我可以更新数据库。

AccountStatus a = new AccountStatus();

UpdateModel(a, formCollection.ToValueProvider());

不幸的是,硬编码对象类型将完全破坏使用接口的原因。

(应用程序的主要目标是能够动态添加新表,例如查找表,而无需执行任何“特殊”操作。这是通过反映加载的程序集并定位实现特定接口或基类的任何类来实现的)

我的策略是在回发时确定对象的具体类型,然后通过反射创建该类型的实例。(我用来确定类型的机制有些原始。我将它作为隐藏字段包含在表单中。欢迎更好的想法。)

当我通过以下任何方法使用反射创建对象的实例时,UpdateModel 都不会更新任何对象。

Type t = {Magically Determined Type}

object b = Activator.CreatorInstance(t);

UpdateModel(b, formCollection.ToValueProvider());


Type t = {Magically Determined Type}

var c = Activator.CreatorInstance(t);

UpdateModel(c, formCollection.ToValueProvider());


Type t = {Magically Determined Type}

IReferenceEntity d = Activator.CreatorInstance(t);

UpdateModel(d, formCollection.ToValueProvider());

注意:我已经验证通过重新选择创建的对象都是正确的类型。

有谁知道为什么会发生这种情况?我有点难过。

如果我真的很“努力”,我可以创建工厂对象,它可以实例化这些引用实体/查找对象中的任何一个。但是,这会破坏应用程序允许透明地添加和发现新查找表的能力,而且不够干净。

此外,我可以尝试从实际的 ReferenceEntity 基类而不是接口派生,但我怀疑这是否会产生任何影响。问题似乎与在模型绑定器中使用反射创建的对象有关。

任何帮助表示赞赏。

安东尼

4

3 回答 3

1

Augi 在 ASP.NET 论坛上回答了这个问题。它只进行了一些小的修改。谢谢奥吉。


问题是 [Try]UpdateModel 方法只允许使用泛型参数指定模型类型,因此它们不允许动态模型类型指定。我为此创建了问题单。

您可以在此处查看 TryModelUpdate 方法的实现。所以编写自己的重载并不难:

public virtual bool TryUpdateModelDynamic<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IDictionary<string, ValueProviderResult> valueProvider) where TModel : class
{
    if (model == null)
    {
        throw new ArgumentNullException("model");
    }
    if (valueProvider == null)
    {
        throw new ArgumentNullException("valueProvider");
    }


    //Predicate<string> propertyFilter = propertyName => BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties);  
    IModelBinder binder = Binders.GetBinder( /*typeof(TModel)*/model.GetType());

    ModelBindingContext bindingContext = new ModelBindingContext()
                                             {
                                                 Model = model,
                                                 ModelName = prefix,
                                                 ModelState = ModelState,
                                                 //ModelType = typeof(TModel), // old  
                                                 ModelType = model.GetType(),
                                                 // new  
                                                 //PropertyFilter = propertyFilter,  
                                                 ValueProvider = valueProvider
                                             };
    binder.BindModel(ControllerContext, bindingContext);
    return ModelState.IsValid;
}
于 2009-09-29T14:41:31.337 回答
0

您的 IReferenceEntity 是否包含属性的设置器以及获取器?我认为如果接口具有属性设置器,最后一个示例将起作用,尽管您必须对其进行强制转换才能使其编译。

Type t = {Magically Determined Type}

IReferenceEntity d = Activator.CreatorInstance(t) as IReferenceEntity;

UpdateModel(d, formCollection.ToValueProvider());

通常它不会在类上设置属性的原因是因为它找不到可通过反射使用的公共 setter 方法。

于 2009-09-24T01:51:51.170 回答
0

只是一个快速的“另一件事要尝试”:

UpdateModel(d as IReferenceEntity, formCollection.ToValueProvider());

不确定这是否可行,我自己也没有尝试过,但这是我想到的第一件事。

如果以后有机会,我会看看 Default Model Binder 代码,看看里面是否有任何明显的东西......

于 2009-09-24T01:51:59.837 回答