I have the following code in my UserRepository:
public async Task RegisterUser(UserAggregate userAggregate)
{
try
{
await _context.UserAggregates.AddAsync(userAggregate);
await _shoppingCartRepository.CreateShoppingCart(userAggregate.UserAggregateId);
await _unitOfWork.Commit();
}
catch (Exception)
{
await _unitOfWork.Rollback();
throw;
}
}
While in the ShoppingCartRepository, I have pretty standard code:
public async Task CreateShoppingCart(int userAggregateId)
{
var shoppingCart = new ShoppingCart
{
UserAggregateId = userAggregateId,
OrderItems = Utils.Serializer(new List<OrderItem>())
};
await _context.ShoppingCarts.AddAsync(shoppingCart);
}
Here is my "Unit of Work" implementation:
public class UnitOfWork : IUnitOfWork
{
private readonly AppDbContext _context;
public UnitOfWork(AppDbContext context)
{
_context = context;
}
public async Task<bool> Commit()
{
return await _context.SaveChangesAsync() > 0;
}
public Task Rollback()
{
return Task.CompletedTask;
}
}
The problem is:
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
MySqlConnector.MySqlException (0x80004005): Cannot add or update a child row: a foreign key constraint fails (
bytestore.shoppingcarts, CONSTRAINTFK_ShoppingCarts_Use rAggregates_UserAggregateIdFOREIGN KEY (UserAggregateId) REFERENCESuseraggregates(Id) ON DELETE CASCADE)
Which means that I'm trying to create a UserAggregateId_FK on ShoppingCart even though the PK on UserAggregate doesn't exist.
What can I do to use Unit of Work and ensure that the transaction will occur entirely?
I was trying to use 2 _context.SaveChangesAsync(), one after each AddAsync, but this is by no means a Unit of Work implementation and could leave users without ShoppingCarts in the database, so I decided to use UoW.
EDIT:
Solved. As pointed by @Svyatoslav Danyliv and @heringer, I was passing UserAggregateId to ShoppingCart instead of UserAggregate. Efcore couldnt use the Id because the user entity was not created yet. Also, Unit of Work is not needed in this case, I just need to call one _context.SaveChangesAsync() now that the code is fixed.
You should have properties of type your repositories in the unit of work class. Only through unit of work you can access your repositories.