I have an ApplicationUser, that has a collection of UserAddress. I simply want to expose endpoints to perform CRUD on these addresses. However when I successfully create an Address (A new row appears in the table, and the User's list of addresses go up by one), when I then go to call another endpoint, the Addresses have reset to 0.
My ApplicationUser model looks like
public class ApplicationUser : IdentityUser
{
// other stuff..
public virtual ICollection<UserAddress> UserAddresses { get; set; } = new List<UserAddress>();
}
My UserAddress model
public class UserAddress
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
// Stuff
public string ApplicationUserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; } = null!;
}
There's no Fluent Api stuff in my DbContext (although I have spent a long time trying some solutions), but I have a public DbSet<UserAddress> UserAddresses { get; set; }
My endpoint for adding an address for a user is simply
[HttpPost("Create")]
public async Task<IActionResult> CreateAddress([FromBody] UserAddressDTO request)
{
var user = await _userManager.GetUserAsync(User);
if (user is null)
{
return Unauthorized(new ApiError("Could not get a user"));
}
var mappedAddress = new UserAddress
{
Line1 = request.Line1,
Line2 = request.Line2,
City = request.City,
Country = request.Country,
Postcode = request.Postcode,
IsPrimaryAddress = request.IsPrimaryAddress,
// I don't usually do this, this probably is not necessary but I've been trying a lot of things
ApplicationUser = user,
ApplicationUserId = user.Id,
};
user.UserAddresses.Add(mappedAddress);
await _context.SaveChangesAsync();
// Inspector says user.UserAddresses has a count of 1 at this point. A row is created with the UserId.
return Ok();
}
And then when I GET it, it's empty.
[HttpGet("All")]
public async Task<IActionResult> GetAll()
{
var user = await _userManager.GetUserAsync(User);
if (user is null)
{
return Unauthorized(new ApiError("Could not get a user"));
}
// Empty array.
var addressesForUser = user.UserAddresses;
return Ok(addressesForUser);
}
Believe me I have read 999 stack overflow posts, I've read my .NET book, Microsoft docs lol. I am going mad. Usually when I'd do a task like this I'd create the new entity (without any references to what has a collection of it), create a new collection of that entity in the class I want it in, and EF would do some magic and it would just work.
Edit: Added the addresses to the Ok in the GET, I was fiddling with the code, I wouldn't normally return the model objects.
CodePudding user response:
Your forgetting to include the UserAddress, that's why the Address are null
var addressesForUser = user.include(e => e.user.UserAddresses);
I believe your call should be var addressesForUser = applicationuser.include(e => e.applicationuser.UserAddresses);
If you're not using autoInclude. As i can't see you including the List of object. asp.net.core does not autoinclude when calling a List of object inside a class. IT has been recently created in EF6 where they implemented AutoInclude in the ModelBuilder
CodePudding user response:
Thank you to RojhatSefdin for giving me the insight to fix this problem. The code that solved it for me is
[HttpGet("All")]
public IActionResult GetAll()
{
var userId = _userManager.GetUserId(User);
var user = _context.Users
.Include(e => e.UserAddresses)
.FirstOrDefault(x => x.Id == userId);
if (user is null)
{
return Unauthorized(new ApiError("Could not get a user"));
}
return Ok(_mapper.Map<IList<UserAddressDTO>>(user.UserAddresses));
}
I'm not 100% sure yet but I believe the User I was getting from the UserManager did not include all the other attributes (lazy loading?), so I changed it to just fetch the Id, then I queried the DB with the .Include() to get what I needed.