Привязка модели - тип во внешней сборке - PullRequest
2 голосов
/ 21 февраля 2012

У меня есть тип в сборке, на который не ссылается базовая библиотека, но на который ссылается веб-приложение.например,

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 никогда не обновляется, даже если значение существует вколлекция форм.

Буду признателен, если кто-нибудь поможет объяснить, почему это не работает и есть ли альтернативный метод.

Спасибо

1 Ответ

3 голосов
/ 21 февраля 2012

Ваша проблема не имеет ничего общего с тем, что тип находится в другой сборке или вы динамически создаете его с помощью 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
    ...
}
...