0

I have a MasterModel class that basically handles a lot of data for partial views across shared layouts. However, when I try and return part of the object (or the entire MasterModel) in a form, such as "Login", no data is passed to the controller.

Here is part of the MasterModel:

public class MasterModel
{
    public Login Login;
    public Account Account;
    public User User;

    public MasterModel()
    {
        Login = new Login();
        Account = new Account();
        User = new User();
    }
}

Here is part of the Login:

public class Login
{
    public string Email {get;set;}
    public string PasswordHash {get; private set;
    public string FName {get;set;}
}

Here is part of the master Layout template that calls the partial view for a login:

@model Models.MasterModel
...
@{
     Html.RenderPartial("../Partials/_Login", Model.Login);
}
...

And here is the _Login partial:

@model Models.Login
<div class="login-bar">

    @if (Model != null && !string.IsNullOrWhiteSpace(Model.FName) )
    {
        var welcomeString = "Welcome back, " + Model.FName;
        <p>@Html.ActionLink(welcomeString, "Overview", "Account")</p>
    }
    else
    {
        using (Html.BeginForm("Login", "Account", FormMethod.Post))
        {
            @Html.AntiForgeryToken()

            <p>
                @Html.Label("Email Address:")
                @Html.EditorFor(login => login.Email, new { @class = "email" })

                @Html.Label("Password:")
                @Html.PasswordFor(login => login.PasswordHash)
                <input type="submit" value="Login" class="button" />

                @if (ViewBag.Message != null)
                {
                    <shim class="red">@ViewBag.Message.ToString()</shim>
                }
            </p>
        }
    }

</div>

Here's the AccountController for the HttpPost for Login too:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(Login login, string returnUrl)
{
    var master = new MasterModel();
    // ModelState is valid. login.Email = "" and login.PasswordHash is still default
    if (ModelState.IsValid && (!string.IsNullOrEmpty(login.Email) && !string.IsNullOrEmpty(login.PasswordHash)))
    {
        var user = await UserManager.FindAsync(login.Email, login.PasswordHash);
        if (user != null)
        {
            await SignInAsync(user, true);
            master.User = user;
            return RedirectToAction("Overview", "Account", master);
        }
    }
    return View(master);
}

I've also tried passing the entire MasterModel to the partial view instead (also changing the @model to use Models.MasterModel) whilst accessing the Login property as masterModel.Login.Email, etc, to no avail.

It's got to be something pretty simple that I'm missing with the binding on the property names, but I'm starting to go code blind now!

Any ideas?

[UPDATE] - Added AccountController

2
  • have you tried using a program like Fiddler to see what information is being passed to where? telerik.com/fiddler Commented Jul 5, 2015 at 17:25
  • Checked out the headers in Chrome and it all seems OK. ...&Email= ...&PasswordHash= ... It's all there. Even took a look at the HTML that got rendered and the ids & names are "Email" and "PasswordHash" respectively. Commented Jul 5, 2015 at 17:58

2 Answers 2

1

After 7 hours of hunting, I can now kick myself.

The login model I posted above was missing one thing that I chose to omit out of speed. Here's what it should have looked like in my original question above:

public class Login
{
    public string Email {get; private set;}
    public string PasswordHash {get; private set;} 
    public string FName {get; private set;}
} 

Changing these to public properties (the PasswordHash slightly differently though) has solved the mystery of the missing object:

public class Login
{
    public string Email {get;set;}
    public string PasswordHash {get; set;} // Not actual implementation. Applies it's own crypto first
    public string FName {get;set;}
}

Hopefully this helps someone else (and myself again in a few weeks!) from banging their head against the wall!

Sign up to request clarification or add additional context in comments.

Comments

0

Everything seems ok.. You might try renaming your parameter in your Login action to something besides login

public async Task<ActionResult> Login(Login model, string returnUrl)

and maybe do the same in your partial

@Html.EditorFor(model => model.Email, new { @class = "email" })

2 Comments

Modified the controller and the view to use "model" and still have the same problem. I had a similar problem with naming conventions a few weeks ago, but can't for the life of me remember what I did to fix it! Going to trawl through previously checked in code and see where I'm going wrong.
Interesting. I changed the AccountController Login(Login model, string returnUrl) to take just the two fields in the response - Login(string email, string passwordHash) and the two items are fed back to the controller correctly. Any idea why the Login object isn't being sent back?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.