I'm not sure why while trying to create an entity which is 1:many EF tries to add new entry in Asp Net Users instead of update 1:many
I have one user
which has many items
SqlException: Violation of PRIMARY KEY constraint 'PK_AspNetUsers'. Cannot insert duplicate key in object 'dbo.AspNetUsers'. The duplicate key value is (cdbb1f2f-ddcf-40c0-97ec-f50f8049d87a).
public class Context : IdentityDbContext
{
public Context(DbContextOptions options) : base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Item> Items { get; set; }
public DbSet<File> Files { get; set; }
}
public class User : IdentityUser
{
public List<Item> Items { get; set; } = new List<Item>();
}
public class Item
{
private Item()
{
}
public Item(string title, User owner, File file)
{
Title = title;
Owner = owner;
File = file;
}
public int Id { get; private set; }
public string Title { get; set; }
public User Owner { get; set; }
public File File { get; set; }
public DateTime CreationDate { get; } = DateTime.Now;
}
And here's where's the problem:
var fileResult = await _file.SaveFile(input.File);
var item = new Item(input.Title, user, fileResult.File);
user.Items.Add(item);
await _context.Items.AddAsync(item);
await _context.SaveChangesAsync();
User is loaded with:
public User GetUser()
{
return _context.Users.FirstOrDefault(x => x.UserName == _http.HttpContext.User.Identity.Name);
}
I tried that:
When I change
public Item(string title, User owner, File file)
{
Title = title;
Owner = owner;
File = file;
}
to just:
public Item(string title, File file)
{
Title = title;
File = file;
}
and let it be handled by:
user.Items.Add(item);
then OwnerId in DB is null
Using:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasMany(x => x.Items)
.WithOne(x => x.Owner);
modelBuilder.Entity<Item>().HasOne(x => x.Owner);
}
is not helping either
The problem was casued by ServiceLifetime
of DbContext
Because User was loaded in Controller
and then thrawn into Service
that was responsible for business logic
I changed
(options => options.UseSqlServer(Configuration["Database:ConnectionString"]), ServiceLifetime.Transient);
to
(options => options.UseSqlServer(Configuration["Database:ConnectionString"]), ServiceLifetime.Scoped);
and it works fine.