'AspNetCore.Identity userManager.FindByNameAsync throws Access Violation Exception upon finding said user
I'm in the process of making a simple user register endpoint using Identity alongside JWT tokens. When using userManager.FindByNameAsync(username), if it doesn't find any user, it works as expected returning null and continuing the process of registration. If there is an already existing user with that same username it throws Access Violation Exception and the server stops. This is the verification where i hardcoded the method's parameter for testing purposes:
User userExists = await _userManager.FindByNameAsync("Andrew");
if (userExists != null)
throw new Exception("User already exists");
Custom IdentityUser from the data access layer:
public class User : IdentityUser
{
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public DateTime JoinedAt {
get { return DateTime.Now; }
set { JoinedAt = value; }
}
public ICollection<ExampleEntity>? ExampleEntities { get; set; }
public ExampleEntity? ExampleEntity { get; set; }
public ICollection<ExampleEntity>? ExampleEntities2 { get; set; }
}
And the AppDbContext both for identity and ExampleEntity
public class AppDbContext : IdentityDbContext<User>
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<User>()
.HasMany(j => j.ExampleEntities)
.WithOne(j => j.User);
builder.Entity<Property>()
.HasMany(p => p.Users)
.WithOne(t => t.ExampleEntity)
.HasPrincipalKey(t => t.Id)
.OnDelete(DeleteBehavior.NoAction);
builder.Entity<ExampleEntity>()
.HasOne(p => p.User)
.WithMany(p => p.ExampleEntities2)
.HasPrincipalKey(t=> t.Id)
.OnDelete(DeleteBehavior.NoAction);
base.OnModelCreating(builder);
}
public override DbSet<User>? Users { get; set; }
public DbSet<ExampleEntity>? ExampleEntities { get; set; }
}
And the error containing the query:
Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (31ms) [Parameters=[@__normalizedUserName_0='?' (Size = 256)], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [a].[Id], [a].[ExampleEntityId], [a].[AccessFailedCount], [a].[BirthDate], [a].[ConcurrencyStamp], [a].[Email], [a].[EmailConfirmed], [a].[JoinedAt], [a].[LockoutEnabled], [a].[LockoutEnd], [a].[Name], [a].[NormalizedEmail], [a].[NormalizedUserName], [a].[PasswordHash], [a].[PhoneNumber], [a].[PhoneNumberConfirmed], [a].[SecurityStamp], [a].[TwoFactorEnabled], [a].[UserName] FROM [AspNetUsers] AS [a] WHERE [a].[NormalizedUserName] = @__normalizedUserName_0 The program '[30000] HomesForAll.exe' has exited with code 3221225477 (0xc0000005) 'Access violation'.
The desired outcome would be for userManager.FindByNameAsync("Andrew") to find the already existing user record, return it using User Entity and stop the process of registration by throwing the exception. I tried changing the AppDbContext settings thinking it would be because there are no columns for ICollection and the models don't match, and to no avail.
Solution 1:[1]
The code:
public DateTime JoinedAt {
get { return DateTime.Now; }
set { JoinedAt = value; }
has a recursive Setter.
This could be the cause of the error.
Try:
public DateTime JoinedAt { get; set; } = DateTime.Now;
This is the same logic and uses the auto getter / setters.
Background:
See https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/using-properties for an overview of property get/set.
The key thing to know is the difference between a Property and a Field. The Field is where the value is stored. A Property is method of one or both of Get / Set to access the underlying Field.
E.g.:
The long version is:
private DateTime _joinedAt;
public DateTime JoinedAt
{
get { return _joinedAt; }
set { _joinedAt = value; }
}
In C# you can implement the above as Auto Properties, by just using (i.e. no body to the get or set methods themselves):
public DateTime JoinedAt { get; set; }
C# compiler creates the _joinedAt field in the background.
In your example, you have not adopted the Auto Properties approach so the Get and Set METHODS get executed as coded. In your case JoinedAt.Set is called by the client with:
user.JoinedAt = {somedate};
Then you Setter effectively calls
this.JoinedAt = value;
which re-invokes the Setter ad infinitum.
This causes an infinite call-stack and a memory failure. This is reported by the Access Violation error you experienced.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
