2

我在程序集中有一个类型,它没有被核心库引用,但被 Web 应用程序引用。例如

namespace MyApp.Models {
    public class LatestPosts {
        public int NumPosts { get; set; }
    }
}

现在我在核心库中有以下代码:

[HttpPost, ValidateAntiForgeryToken]
public ActionResult NewWidget(FormCollection collection) {
    var activator = Activator.CreateInstance("AssemblyName", "MyApp.Models.LatestPosts");
    var latestPosts = activator.Unwrap();

    // Try and update the model
    TryUpdateModel(latestPosts);
}

代码很容易解释,但 latestPosts.NumPosts 属性永远不会更新,即使该值存在于表单集合中。

如果有人可以帮助解释为什么这不起作用以及是否有替代方法,我将不胜感激。

谢谢

4

1 回答 1

3

您的问题与该类型在另一个程序集中或您使用Activator.Create. 以下代码以非常简化的方式说明了该问题:

[HttpPost, ValidateAntiForgeryToken]
public ActionResult NewWidget(FormCollection collection) 
{
    // notice the type of the latestPosts variable -> object
    object latestPosts = new MyApp.Models.LatestPosts();

    TryUpdateModel(latestPosts);

    // latestPosts.NumPosts = 0 at this stage no matter whether you had a parameter
    // called NumPosts in your request with a different value or not
    ...
}

问题源于Controller.TryUpdateModel<TModel>使用typeof(TModel)而不是model.GetType()确定模型类型的事实,如本连接问题中所述(已关闭原因:)by design

解决方法是滚动您的自定义TryUpdateModel方法,该方法的行为与您预期的一样:

protected internal bool MyTryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IValueProvider valueProvider) where TModel : class
{
    if (model == null)
    {
        throw new ArgumentNullException("model");
    }
    if (valueProvider == null)
    {
        throw new ArgumentNullException("valueProvider");
    }

    Predicate<string> propertyFilter = propertyName => new BindAttribute().IsPropertyAllowed(propertyName);
    IModelBinder binder = Binders.GetBinder(typeof(TModel));

    ModelBindingContext bindingContext = new ModelBindingContext()
    {
        // in the original method you have:
        // ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, typeof(TModel)),
        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
        ModelName = prefix,
        ModelState = ModelState,
        PropertyFilter = propertyFilter,
        ValueProvider = valueProvider
    };
    binder.BindModel(ControllerContext, bindingContext);
    return ModelState.IsValid;
}

进而:

[HttpPost, ValidateAntiForgeryToken]
public ActionResult NewWidget(FormCollection collection) 
{
    object latestPosts = new MyApp.Models.LatestPosts();

    MyTryUpdateModel(latestPosts, null, null, null, ValueProvider);

    // latestPosts.NumPosts will be correctly bound now
    ...
}
于 2012-02-21T15:20:12.820 回答