Reputation: 277
I have this weird behaviour when it comes to creating a new users from scratch(registration) and creating new ones from a logged in user that doesn't depend on the DB underneath(in this case I'm using postgreSQL).
But first here's the relationship:
Now both at registration time and logged-in time I do use UserManager
for the creation of the users.
First I create a new UserSetting
, SaveChanges()
and follow by assigning to the AppUser
property UserSettingId
the value of the Id
of the new created entity.
Here's where it fails depending if you are already a registered and logged-in user or you are a new registering user, the code doesn't change:
var userSettings = await _userService.UserSettings.AddAsync(new UserSettings
{
LanguageId = langId,
ThemeId = themeId
});
//wrapper to the Context.SaveChangesAsync()
//failure point!
if (!await _userService.SaveChangesAsync<bool>())
And the exception error(at the creation of a new user from an already logged-in user) talks about a PK_LanguageId
already existing as if I was creating a new Language entity but, as you can see, I'm just assigning just a default langId
to the new UserSetting
. Here's the exception screenshot:
(sorry for the italian, it does auto-translate)
Short translation: duplicated key for Languages
, as if it was trying to insert also a new Language
entity.
And at the Startup
the registered service(_userService
) is transient.
And here's the model classes and their configuration in OnModelCreating
:
public class AppUser : IdentityUser
{
public long UserSettingsId { get; set; }
public virtual UserSettings UserSettings { get; set; }
//...other properties
}
public class UserSettings
{
public long Id { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
public int ThemeId { get; set; }
public virtual Theme Theme { get; set; }
public virtual AppUser AppUser { get; set; }
}
public class Theme
{
[Key]
public int Id { get; set; }
[StringLength(10)]
public string Type { get; set; }
}
public class Language
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
[MaxLength(2)]
public string Code { get; set; }
}
builder.Entity<AppUser>().HasKey(u => u.Id);
builder.Entity<AppUser>().HasOne(u => u.UserSettings).WithOne(us => us.AppUser);
builder.Entity<UserSettings>().HasKey(us => us.Id);
builder.Entity<UserSettings>().Property(us => us.Id).ValueGeneratedOnAdd();
builder.Entity<UserSettings>().HasOne(us => us.Language);
builder.Entity<UserSettings>().HasOne(us => us.Theme);
What I'm doing wrong? Why it does behave so differently? Is there a different way to create users from an already logged-in user?
Upvotes: 3
Views: 293
Reputation: 277
Found the source of the behaviour, it's the damn UserManager.GetUserAsync()
, which I user to get the currently logged-in user with added UserSettings
later on to propagate the "Admin"'s settings to the new creating account.
GetUserAsync(User)
does return an entity that does keep being tracked(instead of, as I thought, being like AsNoTracking()
). So consequently when I did fill it's UserSetting
I did in fact added a new UserSetting
with new sub classes properties!
Sooo, never expect UserManager
give you the user without tracking!
Thanks for everyone involved with the effort.
Upvotes: 2
Reputation: 3208
The problem might be that you are adding object to the database context which marks it as added. Because database context isn't aware of existence Language
and Theme
objects it is trying to insert all of them.
It may be issue with postgreSQL provider. You might want to check opened issues.
I'd suggest to try loading Language
object from database first and then try to add new UserSettings
.
Upvotes: 2