0

Goodevening. I am stuck on something I can't seem to fix my self. I created a razor page called Address.cshtml.cs (model) and Address.cshtml (view) in my project for users to be able to add their user information AFTER registering. And var result = await _userManager.UpdateAsync(user); doesn't seem to work for it. I tried two ways to update it in the database:

First try

  [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> AccountChange(UserModel model)
        {
            if (ModelState.IsValid)
            {

                // Get the current application user
                var user = await _userManager.GetUserAsync(User);

                //Update the details
                user.name = model.name;
                user.surname = model.surname;
                user.street = model.street;
                user.streetnumber = model.streetnumber;
                user.city = model.city;
                user.zipcode = model.zipcode;

                // Update user address
                var result = await _userManager.UpdateAsync(user);
            }


            //await _signInManager.RefreshSignInAsync(User);
            _logger.LogInformation("User added their address information successfully.");
            StatusMessage = "Your address information has been added.";

            return RedirectToPage();

        }
    }

Second try

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(UserModel model)
{
    if (ModelState.IsValid)
    {
        UserModel u = await _userManager.FindByIdAsync(model.Id);
        u.name = model.name;
        u.surname = model.surname;
        u.street = model.street;
        u.streetnumber = model.streetnumber;
        u.city = model.city;
        u.zipcode = model.zipcode;
        await _userManager.UpdateAsync(u);
        return RedirectToAction("Index");
    }
    return RedirectToPage();
}

How can I fix this? I've added all of the necessary code below.

Address.cshtml.cs

namespace bytme.Areas.Identity.Pages.Account.Manage
{
    public class AddressModel : PageModel
    {
        private readonly UserManager<UserModel> _userManager;
        private readonly SignInManager<UserModel> _signInManager;
        private readonly ILogger<AddressModel> _logger;

        public AddressModel(
            UserManager<UserModel> userManager,
            SignInManager<UserModel> signInManager,
            ILogger<AddressModel> logger)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _logger = logger;
        }

        [BindProperty]
        public InputModel Input { get; set; }

        public string ReturnUrl { get; set; }

        [TempData]
        public string StatusMessage { get; set; }

        public class InputModel
        {
            [Required]
            [DataType(DataType.Text)]
            [Display(Name = "Name")]
            [StringLength(100, ErrorMessage = "Invalid input. Maximum is 100 characters.")]
            public string name { get; set; }

            [Required]
            [DataType(DataType.Text)]
            [Display(Name = "Surname")]
            [StringLength(100, ErrorMessage = "Invalid input. Maximum is 100 characters.")]
            public string surname { get; set; }

            [Required]
            [DataType(DataType.Text)]
            [Display(Name = "Street")]
            [StringLength(48, ErrorMessage = "The longest street name in the Netherlands is 48 characters.")]
            public string street { get; set; }

            [Required]
            [DataType(DataType.Text)]
            [Display(Name = "House Number")]
            [StringLength(5, ErrorMessage = "The longest house number in the Netherlands is 5 characters.")]
            public string streetnumber { get; set; }

            //[DataType(DataType.Text)]
            //[Display(Name = "House Number Addition", Description = "For example A or II")]
            //[StringLength(6, ErrorMessage = "
            //public string streetnumberadd { get; set; }

            [Required]
            [DataType(DataType.Text)]
            [Display(Name = "City")]
            [StringLength(28, ErrorMessage = "The longest place name in the Netherlands is 28 characters.")]
            public string city { get; set; }

            [Required]
            [DataType(DataType.PostalCode)]
            [Display(Name = "Postal Code")]
            [RegularExpression(@"^[1-9][0-9]{3}\s?[a-zA-Z]{2}$", ErrorMessage = "Invalid zip, for example: 1234AB")]
            public string zipcode { get; set; }
        }

        public void OnGet(string returnUrl = null)
        {
            ReturnUrl = returnUrl;
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> AccountChange(UserModel model)
        {
            if (ModelState.IsValid)
            {

                // Get the current application user
                var user = await _userManager.GetUserAsync(User);

                //Update the details
                user.name = model.name;
                user.surname = model.surname;
                user.street = model.street;
                user.streetnumber = model.streetnumber;
                user.city = model.city;
                user.zipcode = model.zipcode;

                // Update user address
                var result = await _userManager.UpdateAsync(user);
            }


            //await _signInManager.RefreshSignInAsync(User);
            _logger.LogInformation("User added their address information successfully.");
            StatusMessage = "Your address information has been added.";

            return RedirectToPage();

        }
    }
}

Address.cshtml

@page
@model AddressModel
@inject SignInManager<UserModel> SignInManager
@using Microsoft.AspNetCore.Identity
@using bytme.Models;
@{
    ViewData["Title"] = "Add Address Information";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@Html.Partial("_StatusMessage", Model.StatusMessage)
@{
    var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any();
}
<div>
    <h3>Change your account settings</h3>
    <hr />
    <div class="row">
        <div class="col-md-3">
            <partial name="_ManageNav" />
        </div>
        <div class="col-md-9">
            <div class="row">
                <div class="col-md-6">
                    <h4>@ViewData["Title"]</h4>
                    <form id="change-password-form" method="post">
                        <div asp-validation-summary="All" class="text-danger"></div>
                        <div class="form-group">
                            <label asp-for="Input.name"></label>
                            <input asp-for="Input.name" class="form-control" />
                            <span asp-validation-for="Input.name" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label asp-for="Input.surname"></label>
                            <input asp-for="Input.surname" class="form-control" />
                            <span asp-validation-for="Input.surname" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label asp-for="Input.street"></label>
                            <input asp-for="Input.street" class="form-control" />
                            <span asp-validation-for="Input.street" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label asp-for="Input.streetnumber"></label>
                            <input asp-for="Input.streetnumber" class="form-control" />
                            <span asp-validation-for="Input.streetnumber" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label asp-for="Input.city"></label>
                            <input asp-for="Input.city" class="form-control" />
                            <span asp-validation-for="Input.city" class="text-danger"></span>
                        </div>
                        <div class="form-group">
                            <label asp-for="Input.zipcode"></label>
                            <input asp-for="Input.zipcode" class="form-control" />
                            <span asp-validation-for="Input.zipcode" class="text-danger"></span>
                        </div>
                        <button type="submit" class="btn btn-default">Submit</button>
                    </form>
                </div>
            </div>

        </div>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

UserModel.cs

namespace bytme.Models
{
    public class UserModel : IdentityUser
    {
        public override string Id { get; set; }
        public override string Email { get; set; }
        public override string UserName { get; set; }
        public override string PasswordHash { get; set; }
        public string zipcode { get; set; }
        public string city { get; set; }
        public string street { get; set; }
        public string streetnumber { get; set; }
        public string name { get; set; }
        public string surname { get; set; }
    }
}

ApplicationDbContext.cs

namespace bytme.Data
{
    public class ApplicationDbContext : IdentityDbContext<UserModel>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            // Customize the ASP.NET Identity model and override the defaults if needed.
            // For example, you can rename the ASP.NET Identity table names and more.
            // Add your customizations after calling base.OnModelCreating(builder);
        }

        public DbSet<bytme.Models.Item> Items { get; set; }
        public DbSet<bytme.Models.ItemCategories> ItemCategories { get; set; }
        public DbSet<bytme.Models.UserModel> UserModels { get; set; }
        public DbSet<bytme.Models.OrderHistory> OrderHistories { get; set; }
        public DbSet<bytme.Models.OrderMain> OrderMains { get; set; }
        public DbSet<bytme.Models.OrderStatus> OrderStatuses { get; set; }
        public DbSet<bytme.Models.WishlistModel> WishlistModels { get; set; }
        public DbSet<bytme.Models.ShoppingCartModel> ShoppingCartModels { get; set; }
    }
}
Floor
  • 216
  • 3
  • 16
  • is there any particular error or just not updating? – Ziaul Kabir Fahad Oct 19 '18 at 18:30
  • @ZiaulKabirFahad Hi. It is not updating, no errors nowhere. – Floor Oct 19 '18 at 18:31
  • @floorvmt Two things, I found out the following information `If you leave any of the fields for ApplicationUser OR IdentityUser null the update will come back as successful but wont save the data in the database.` Are you sure, that passed instance in method is allright, no null property? Also, nice discussion about possible problem of dbContext, check out top answer [link](https://stackoverflow.com/questions/20444022/updating-user-data-asp-net-identity?answertab=active#tab-top). The second thing, dont you want to try update the user without userManager? ASP.Net Identity lies on EF (continue) – Arsiwaldi Oct 19 '18 at 20:53
  • @floorvmt (continue) So you can just create `new dbContext`, attaching your entity to UserModel set, set state of entity to modified and `SaveChanges()`. Also, I think that there exists som extension method `InsertOrUpdate()`. It's worth testing at least. – Arsiwaldi Oct 19 '18 at 20:54
  • @Arsiwaldi Oh yeah, I see in Postgres that every property I want to update is already set to an null, do I have to make it in the model to 'NOT NULL' or something like that? – Floor Oct 19 '18 at 22:06
  • @Arsiwaldi And on the updating the user, I thought because the register and log-in are created with the userManager, I thought I had to do the same as well for updating the user – Floor Oct 19 '18 at 22:07

1 Answers1

1

After checking the code, I found several problems.

1. I have doubts about your post method AccountChange. The following conventions should be followed. According to Microsoft docs about razor pages, there are generated several default method handlers, like: OnGet OnPost OnGetAsync OnPostAsync etc.

However, if you want to use custom handler name, it has to follow some naming convention as well.

Method should start with OnPost[Get,...]<Handlername>[Async|NoAsync](its optional).

So your method shoud be named like OnPostAccountChangeAsync()

If you define such a method, you has to tell a view, that you want to use the specified handler. You tell it with asp-page-handler directive, so your form shoud looks like this:

<form id="change-password-form" method="post">
   //...
   <button type="submit" class="btn btn-default" asp-page-handler="AccountChange">Submit</button>
</form>


2. You are binding your property Input

[BindProperty]
public InputModel Input { get; set; }

However, in your method OnPostAccountChangeAsync(), you are trying to accces in the parameter instance of type UserModel, which gonna be null. Its not binded. In the good case you will get a null exception. In bad one, you will update your entity with null properties. So your method OnPostAccountChangeAsync() should accept at parameter instance of InputModel or you can access this property directly inside the body of method (you can get rid of parameter)

OnPostAccountChangeAsync(InputModel Input)
{
 //...
}


3. Its minor thing, however, it improves the readability of your code. Please be consistent in naming of your variables. Consitency makes better orientation in your code. In Csharp properties should start with a capital letter (PascalCasing). Do not hesitate, and check C# naming convention


To your question about UserManager in comment section:
You can look at Identity as wrapper (rather say api), which provides useful methods for user management, besides that it offer other things. As I said, if you look at your package dependecies, you can spot that Entity Framework is shipped within Identity nuget package. What does it mean? It opens you possibility to not be only dependent on some "mysterous" Identity. You can clearly use Entity Framework for e.g. saving new user into table AspNetUsers. Clearly said, you are not limited. However, if you already use Identity, it is better to use its available methods.

Arsiwaldi
  • 513
  • 1
  • 7
  • 23
  • Thanks for answering @Arsiwaldi. It still doesn't work me though. Adding the information does work when I let them add them when registering. Although, that is not what I wanted.. I made a new question for it.. https://stackoverflow.com/questions/52906582/not-able-to-update-aspnetusers-identity-with-custom-properties – Floor Oct 20 '18 at 14:14