1

I get a NullReferenceException on Roles, and Subscriptions, I am not sure why, my Model.State check is false, so it is invalid. I have passed the same view model to my post method as my get method has.

Form

<div class="form-group">
    @Html.LabelFor(m => m.User.UserRoles)
    @Html.DropDownListFor(m => m.User.SelectedUserRoleId, Model.User.UserRoles, "Role", new { id = "txtBusinessName", @class = "form-control", placeholder = "Business Name" })
</div>

<div class="form-group">
    @Html.LabelFor(m => m.Business.Subscriptions)
    @Html.DropDownListFor(m => m.Business.SelectedSubscriptionId, Model.Business.Subscriptions, "Subscription Type", new { id = "txtBusinessName", @class = "form-control", placeholder = "Business Name" })
</div>

Model

public class RegisterationViewModel
{
    public BusinessViewModel Business { get; set; }
    public UserViewModel User { get; set; }
}

UserViewModel properties with error

public Guid SelectedUserRoleId {get; set;}

[Display(Name = "Role")]
[Required]
public IEnumerable<SelectListItem> UserRoles { get; set; } 

BusinessViewModel properties with error

public Guid SelectedSubscriptionId {get; set; }
[Display(Name = "Subscription Type")]
[Required]
public IEnumerable<SelectListItem> Subscriptions { get; set; }

Get Method

[HttpGet]
public ActionResult Register()
{
    var businessSubscriptionViewModels = new List<BusinessSubscriptionViewModel>();
    var userRoleViewModels = new List<UserRoleViewModel>();

    var registrationViewModel = new RegisterationViewModel();
    businessSubscriptionViewModels.AddRange(
        _subscriptionServiceClient.GetBusinessSubscriptionContracts()
            .Select(Mapper.Map<BusinessSubscriptionContract, BusinessSubscriptionViewModel>));

    registrationViewModel.Business = new BusinessViewModel
    {
        Subscriptions = new SelectList(businessSubscriptionViewModels, "Id", "Name")
    };

    userRoleViewModels.AddRange(
        _userRoleServiceClient.GetUserRoles().Select(Mapper.Map<UserRoleContract, UserRoleViewModel>));

    registrationViewModel.User = new UserViewModel
    {
        UserRoles = new SelectList(userRoleViewModels, "Id", "Name")
    };
    return View(registrationViewModel);
}

Post Method

[HttpPost]
public ActionResult Register(RegisterationViewModel registerationViewModel)
{
    if (!ModelState.IsValid)
    {
        return View();
    }

    var businessContract = Mapper.Map<BusinessViewModel, BusinessContract>(registerationViewModel.Business);
    var registeredBusiness = _businessRegistrationServiceClient.CreateBusiness(businessContract);

    var userContract = Mapper.Map<UserViewModel, UserContract>(registerationViewModel.User);
    var registeredUser = _userServiceClient.CreateUser(businessContract.ID, userContract);


    return View(registerationViewModel);
}
Nkosi
  • 191,971
  • 29
  • 311
  • 378
ife labolz
  • 1,320
  • 3
  • 25
  • 66
  • Maybe the modelbinder can't bind the `string SelectListItem.Value` as a Guid? To test this, change `SelectedUserRoleId` to `string` in your Model. – Georg Patscheider Jun 16 '16 at 14:42
  • I also notice that you pass the same value for `id` in the HtmlAttributes of both DropDownLists. Let MVC set the ID so it is recognized by the modelbinder. – Georg Patscheider Jun 16 '16 at 14:45
  • In your post method you "return View();" with no model. I'm not an expert but it seems to me that might be an issue. – Ian Jun 16 '16 at 15:29

2 Answers2

1

Your UserRoles and Subscriptions properties will be null after posting because only the value of the selected option the select list will be posted, not the select list itself. Also, depending on what is or is not posted, the Business and/or User properties on your view model may be null as well. The modelbinder will only new up an instance for those properties if at least one property on those instances was posted. If, for example, someone left the whole "user" section of the form blank, then User will be null, and attempting to access any property on User will cause a NullReferenceException.

You either need to ensure that Business and User always have a value (via the constructor of your view model or custom getters and setters) or you need to explicitly check for null values before accessing any of their members.

Chris Pratt
  • 207,690
  • 31
  • 326
  • 382
  • Hi Chris, thanks for your response, there are other properties in the Business and User other than Subscriptions and UserRoles, those properties have values, and the SelectedUserRoleId and SelectedSubscriptionId also have values which are the right Guid. It is just the SelectListItem properties that are null. I guess my question to you now is how do I ensure this is not null ? Do i have to change the UI a little ? – ife labolz Jun 16 '16 at 17:19
  • 1
    No. The same method you use in the GET action to populate them should be employed in the POST action as well. – Chris Pratt Jun 16 '16 at 17:46
0

I figured it out, the mistake was i have a Required validation check on the SelectListItems property. But on a post the value the select list is binds to is the only thing that will be posted as Chris suggested.

If i want to maintain the same required validation, I have set the validation to the Selected property which the SelectListItem binds to. This way since the SelectListItem is null, the SelectedUserRoleId or SelectedBusinessSubscriptionId will not be.

Simple fix

[Required]
public Guid SelectedUserRoleId {get; set;}

[Display(Name = "Role")]
public IEnumerable<SelectListItem> UserRoles { get; set; } 

[Required]
public Guid SelectedSubscriptionId {get; set; }

[Display(Name = "Subscription Type")]
public IEnumerable<SelectListItem> Subscriptions { get; set; }
ife labolz
  • 1,320
  • 3
  • 25
  • 66