Reputation: 131
I have an issue, when I'm creating a user, it doesn't initialize the property that is list<> type - I get the following error:
An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an object.
AspNetCore.Views_Home_Index.ExecuteAsync() in Index.cshtml, line 53
The error appears only when I'm registered and logged in! I think that's important.
As you can expect the line with the error is
foreach (var currencyUser in favoriteCurrency)
so it means that favoriteCurrency is null, but it shouldn't be.
EDIT: Here is the full context:
@using X.PagedList.Mvc.Core;
@using X.PagedList;
@using X.PagedList.Mvc.Common
@using Microsoft.AspNetCore.Identity
@using WalutyBusinessLogic.Models
@inject SignInManager<User> SignInManager
@inject UserManager<User> UserManager
@{
ViewData["Title"] = "Home Page";
}
@{
User applicationUser = null;
List<UserCurrency> favoriteCurrency = null;
if (SignInManager.IsSignedIn(User))
{
applicationUser = await UserManager.GetUserAsync(User);
favoriteCurrency = applicationUser.UserFavoriteCurrencies;
}
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
</div>
<form asp-controller="Home" asp-action="Index" method="get">
<div class="md-form mt-0">
<input name="searchString" class="form-control" type="text" value="@ViewBag.searchFilter" placeholder="Search by code" aria-label="Search">
</div>
</form>
<table class="table">
<thead>
<tr>
<th scope="col">Currency code</th>
<th scope="col">Full name</th>
</tr>
<tr></tr>
</thead>
<tbody>
@foreach (var currencyInfo in ViewBag.SinglePageOfCurrencyInfo)
{
<tr>
<td>@currencyInfo.Code</td>
<td>@currencyInfo.Name</td>
@if (SignInManager.IsSignedIn(User))
{
bool isAlreadyIn = false;
foreach (var currencyUser in favoriteCurrency)
{
if (currencyInfo.Id == currencyUser.CurrencyId && !isAlreadyIn)
{
isAlreadyIn = true;
break;
}
}
if (isAlreadyIn)
{
<td><a asp-controller="Favorites" asp-action="delete" asp-route-id="@currencyInfo.Id">Remove from Favorites</a></td>
}
else
{
<td><a asp-controller="Favorites" asp-action="add" asp-route-id="@currencyInfo.Id">Add to Favorites</a></td>
}
}
</tr>
}
</tbody>
</table>
@Html.PagedListPager((IPagedList)ViewBag.SinglePageOfCurrencyInfo,
page => Url.Action("Index", new { page = page, searchString = ViewBag.searchFilter }),
new PagedListRenderOptions
{
LiElementClasses = new string[] { "page-item" },
PageClasses = new string[] { "page-link" },
MaximumPageNumbersToDisplay = 5,
EllipsesFormat = ""
})
Here is my registration method - scaffolded from identity, but slightly changed
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
var user = new User { UserName = Input.Email, Email = Input.Email, UserFavoriteCurrencies = new List<UserCurrency>()};
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
Log.Logger.Information($"User {Input.Email} has been created.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = user.Id, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
as you can see in this part i'am indicating the creation:
var user = new User { UserName = Input.Email, Email = Input.Email, UserFavoriteCurrencies = new List<UserCurrency>()};
but it doesn't go through.
Here are my classes:
User:
public class User : IdentityUser
{
public virtual List<UserCurrency> UserFavoriteCurrencies { get; set; }
}
UserCurrency:
public class UserCurrency
{
public int UserId { get; set; }
public User User { get; set; }
public int CurrencyId { get; set; }
public Currency Currency { get; set; }
}
Currency if needed:
public class Currency
{
public int Id { get; set; }
public string Name { get; set; }
public List<CurrencyRecord> ListOfRecords { get; set; }
public List<UserCurrency> FavoritedByUsers { get; set; }
}
If user is logged in I receive mentioned error. What am I doing wrong?
Upvotes: 2
Views: 1464
Reputation: 16689
Can you change the init logic to create the user and then set the collection?
var user = new User {
UserName = Input.Email,
Email = Input.Email
};
user.UserFavoriteCurrencies = new List<UserCurrency>()
You code looks like it should all work, but the above logic allows you to debug past the User instantiation and deliberately sets the value of the property.
I would normally change the collection properties to auto-init, this simplifies serialization and init logic like yours which might solve your issue too.
public class User : IdentityUser
{
public virtual List<UserCurrency> UserFavoriteCurrencies { get; set; } = new List<UserCurrency>();
}
In initialiazation scenarios like this, it is hard to follow along at home, especially with line number references in error messages and no line numbers in the code you have posted.
I suspect in your UserManager.CreateAsync(?unknownType? user, string password)
that your custom property is ignored, usually because in this implementation CreateAsync would take an interface or IdentityUser, or in any case the pattern of CreateAsync within usermanager tends to be "Take the generic properties passed in and create the actual user identity as a new object.
If this is the case then your should override the CreateAsync
method to manage your custom input, or after calling CreateAsync
then initialise the returned user object that was created.
To illustrate the point, the user
object you created and passed in to the CreateAsync
method will not usually be the same user object instance in the response from CreateAsync
.
This is all based on assumptions, based on the little code you have provided. If this information is not sufficient, isolate the line of code for use where you first detect that
UserFavoriteCurrencies
is null in your user object, and also indicate the last line of code where it has the values you are expecting. This information itself was hard to recognise from your post.
Upvotes: 1