DefaultModelBinder behaviour when property absent

2019-09-04 13:57发布

问题:

I have a model like the following:

public class TestViewModel
{
  string UpdateProperty { get; set; }
  string IgnoreProperty { get; set; }
  ComplexType ComplexProperty { get; set; }
}

where

public class ComplexType
{
  long? Code { get; set; }
  string Name { get; set; }
}

My controller action:

public Edit(int id, FormColleciton formCollection)
{
  var model = service.GetModel(id);
  TryUpdateModel(model);

  //...
}

When calling the Edit action I have a formCollection parameter containing only a key/value for UpdateProperty.

After the call to TryUpdateModel UpdateProperty is set correctly, IgnoreProperty is left un-touched but ComplexProperty is set to null, even if it previously had a value.

Should TryUpdateModel() only modify properties that are a part of the request? If this is not the case what is the best way to work around this so ComplexProperty is only modified if it is included in the request?


After it was pointed out by Darin that the test case above didn't demonstrate the problem I have added a scenario where this problem really occurs:

public class TestViewModel
{
    public List<SubModel> List { get; set; }
}

public class SubModel
{
    public ComplexType ComplexTypeOne { get; set; }
    public string StringOne { get; set; }
}

public class ComplexType
{
    public long? Code { get; set; }
    public string Name { get; set; }
}

Controller Action:

public ActionResult Index()
{
    var model = new TestViewModel
                    {
                        List = new List<SubModel> { 
                            new SubModel{
                                ComplexTypeOne = new ComplexType{Code = 1, Name = "5"},
                                StringOne = "String One"
                            } 
                        }
                    }; 

    if (TryUpdateModel(model)) { } 

    return View(model);
}

Sending this request:

/Home/Index?List[0].StringOne=test

updates SubModel.StringOne property but sets ComplexTypeOne to null, even though it is not included in the request.

Is this expected behaviour (given this does not happen unless an enumerable of complex types is used)? How best to work around this?

回答1:

There must be something wrong with your test case as I was unable to reproduce it. Here's what I tried:

Model (notice that I use public properties):

public class TestViewModel
{
    public string UpdateProperty { get; set; }
    public string IgnoreProperty { get; set; }
    public ComplexType ComplexProperty { get; set; }
}

public class ComplexType
{
    public long? Code { get; set; }
    public string Name { get; set; }
}

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new TestViewModel
        {
            IgnoreProperty = "to be ignored",
            UpdateProperty = "to be updated",
            ComplexProperty = new ComplexType
            {
                Code = 1,
                Name = "5"  
            }
        };

        if (TryUpdateModel(model))
        {

        }
        return View();
    }
}

Now I send the following request: /home/index?UpdateProperty=abc and inside the condition only the UpdateProperty is modified with the new value from the query string. All other properties, including the complex property, are left untouched.

Also notice that the FormCollection action parameter is useless.