MVC UpdateModel и подклассы против базового класса - PullRequest
4 голосов
/ 26 ноября 2010

Я хочу использовать метод UpdateModel для подкласса, который извлекается во время выполнения, было бы замечательно, если бы кто-то мог пролить свет на то, делаю ли я его полный хэш и / или действительно ли яЯ пытаюсь сделать это возможно.

Я использую универсальное действие для управления проверкой группы частичных представлений;Я пытаюсь избавиться от конкретных действий при частичном просмотре.

Каждое частичное представление имеет уникальную модель, производную от базовой модели:

public class ModelA : ModelBase{
     [Required]
     public string SomeStringProperty{get;set;}
...
}
public class ModelB : ModelBase{
     [Required]
     public DateTime? SomeDateProperty{get;set;}
...
}
public class ModelBase{
     public Guid InstanceId{get;set;}
}

Я использую FormCollection в действии, чтобы получить отправленные элементы формы и их значения, в том числетип модели, которую View должен использовать для проверки своего запроса. Игнорируем последствия этого для безопасности для этого примера, я знаю о них, и это только внутреннее доказательство концепции

    [HttpPost]
    public ActionResult ChangeCaseState(int id, FormCollection formCollection)
    {
        Guid instanceId = new Guid(formCollection["instanceId"]);
        string modelType = formCollection["modelType"];
        //Return a specific Model class based on the event/modelType
        var args = GetStateModelClass(modelType, instanceId);

        try
        {
            UpdateModel(args);
            if(Model.IsValid){
             ...
        }
        catch (Exception)
        {
            return View("~/Views/Shared/StateForms/" + modelType + ".ascx", args);
        }...

А вот код, который я использую длявернуть подкласс на основе modelType, переданного в контроллер.

private static ModelBase StateModelClassFactory(string stateModelTypeName, Guid instanceId)
        {
            switch (stateModelTypeName)
            {
                case "modelTypeA":
                    return new ModelA(workflowInstanceId);
                case "modelTypeB":
                    return new ModelB(workflowInstanceId);
    ...
    }

Поскольку тип возвращаемого значения метода StateModelClassFactory относится к базовому классу, хотя я на самом деле возвращаю подкласс, связыватель моделииспользуемый методом UpdateModel, связывается только со значениями внутри базового класса.

Любые идеи о том, как я могу решить эту проблему?

ОБНОВЛЕНИЕ:

Я создал Связыватель модели клиента:

public class CustomModelBinder : DefaultModelBinder
    {
      public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {

И присвоил новый связыватель модели правильному базовому классу, чтобы увидеть, что происходит под капотом:

ModelBinders.Binders.Add(typeof(ModelBase), new CaseController.CustomModelBinder());

Когда я отлаживаю связыватель модели и проверяю bindingContext, свойство Model представляетправильный подкласс, но свойство ModelType принадлежит базовому классу.Должен ли я смотреть на изменение ModelType в методе BindModel?Если да, то есть какие-то указатели на то, как это сделать, установщик для ModelType, похоже, стал избыточным.Я также заметил, что SomeDateProperty из подкласса на самом деле находится в свойстве PropertyMetadata .... Кажется, это так близко к поведению, как мне хотелось бы.

Ответы [ 3 ]

5 голосов
/ 27 мая 2011

Я только что натолкнулся на эту конкретную проблему и обнаружил, что лучшим общим подходом было бы просто привести вашу модель к dynamic при передаче ее в UpdateModel:

[HttpPost]
public ActionResult ChangeCaseState(int id, FormCollection formCollection)
{
    ...try
    {
        UpdateModel((dynamic)args);//!!notice cast to dynamic here
        if(Model.IsValid){
         ...
    }
    catch...

Это, кажется, устанавливает вседоступные свойства моего типа, независимо от того, деласкарирована ли моя переменная с базовым типом.

Для этой проблемы в CodePlex хранится рабочий элемент: http://aspnet.codeplex.com/workitem/8277?ProjectName=aspnet

2 голосов
/ 29 ноября 2010

Так что я думаю, что решил свою проблему. В основном из-за способа, которым я извлекаю класс Model перед вызовом UpdateModel, связыватель модели связывает BaseClass, хотя Model и принадлежал к SubClass - это код, который я использовал для решения своей конкретной проблемы:

  public class SubClassModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var model = bindingContext.Model;
        var metaDataType = ModelMetadataProviders.Current.GetMetadataForType(null, model.GetType());
        bindingContext.ModelMetadata = metaDataType;
        bindingContext.ModelMetadata.Model = model;

        return base.BindModel(controllerContext, bindingContext);
    }
}

А в Global.asax:

ModelBinders.Binders.Add(typeof(ModelBase), new SubClassModelBinder ());

Спасибо Дарину за его начальный указатель.

0 голосов
/ 26 ноября 2010

Чтобы решить эту проблему, вы можете написать пользовательский механизм связывания для базового типа, который на основе значения свойства строки вернет правильный дочерний экземпляр.

...