【问题标题】:Model Binding - Type in External Assembly模型绑定 - 输入外部装配体
【发布时间】:2012-02-21 13:47:58
【问题描述】:

我在程序集中有一个类型,它没有被核心库引用,但被 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 属性永远不会更新,即使该值存在于表单集合中。

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

谢谢

【问题讨论】:

    标签: asp.net-mvc model-binding updatemodel


    【解决方案1】:

    您的问题与该类型在另一个程序集中或您使用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() 来确定模型类型,如this connect issue 中所述(已关闭原因: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
        ...
    }
    

    【讨论】:

    • 当我面对这样的一些解决方案时,我意识到我离成为一个真正的优秀程序员还很遥远。在这个问题上迷失了一天。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-03
    • 1970-01-01
    • 2017-06-16
    • 1970-01-01
    • 1970-01-01
    • 2017-03-21
    • 2014-10-21
    相关资源
    最近更新 更多