简体   繁体   中英

Difference between

I am creating a simple login page by email and password. I have a class LoginViewModel that will have a User class as a member variable inside it. The class contains the emailAddress. The password is inside the main LoginViewModel. My User class reference is like this:

public User User { get; set; }

when the user fills the email address and password and hit submit, the LoginViewModel correctly binds the field to the email address inside the User class from the view:

@Html.TextBoxFor(m => m.User.Email) // m is the LoginViewModel model

I want to know why it doesn't work if I had the code above looked like this instead:

public User User = new User();

It shows the email inside the User instance as null value. I know that the use of the constructor is probably better than both, but what is the difference between these two.

EDIT#1: On posting at the "Login" action method, this finds the value I entered for the email field in the model:

public User User { get; set; }

this one does NOT:

public User User = new User(); // --> because of this email field value shows up null

This is a feature of the DefaultModelBinder which will only bind properties with public getter/setters. If you explore the source code, the process includes initializing a new instance of your model and then attempting to set the value of its properties. The key part here is

protected virtual void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
{
  ...
  if (!propertyDescriptor.IsReadOnly && !isNullValueOnNonNullableType)
  {
    ...
    propertyDescriptor.SetValue(bindingContext.Model, value) // this is where the value is set
    ...
  }
  ...
}

When you use public User User = new User(); you only creating a field and the IsReadOnly property of its PropertyDescriptor will return false so the code in the if block is never executed and the value of User.Email is null (the default value for string )

I want to know why it doesn't work if I had the code above looked like this instead:

public User User = new User();

Because the infrastructure looks for the set method. It needs to be property with public setter.

The peace of code provided by @Stephen doesn't describe the heart of problem. Here is the DefaultModelBinder 's method which tries to bind properties of model.

private void BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
     IEnumerable<PropertyDescriptor> properties = GetFilteredModelProperties(controllerContext, bindingContext);
     foreach (PropertyDescriptor property in properties)
     {
          BindProperty(controllerContext, bindingContext, property);
     }
}

Here we see that GetFilteredModelProperties tries to get PropertyDescriptor which by chain of methos calls ends up by method call TypeDescriptor.GetProperties which returns the properties of type not the fields.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM