Sorry, my English is poor.
If the customer's email has unique constraint.
When call RegisterCustomer api with duplicate email,
the method in UnitOfWork.CommitAsync will throw DbUpdateException,
how to handle this and return friendly message to frontend, like this
{
errorCode: 409001
message: Duplicated email.
}
the errorCode is unique which defined by application,
and has a api document for frontend, like this
errorCode |
message |
409001 |
Duplicated email |
401001 |
Unauthorized |
...... |
...... |
frontend use errorCode to show user friendly message.
I found most solution is,
public class RegisterCustomerCommandHandler : IRequestHandler<RegisterCustomerCommand, CustomerDto>
{
......
public async Task<CustomerDto> Handle(RegisterCustomerCommand request, CancellationToken cancellationToken)
{
var customer = new Customer(request.Email, request.Name, this._customerUniquenessChecker);
if (EmailAlreadyExist())
{
throw new EmailAlreadyExistException(request.Email);
}
await this._customerRepository.AddAsync(customer);
await this._unitOfWork.CommitAsync(cancellationToken);
return new CustomerDto { Id = customer.Id.Value };
}
}
and use a ExceptionFilter to catch EmailAlreadyExistException.
But this can't handle concurrent insert,
if two requests send at same time with same email,
two requests will pass EmailAlreadyExist,
the method in UnitOfWork.CommitAsync will throw DbUpdateException,
this solution don't solve problem.
Maybe this happen rarely, I can return a response with 500(Internal Server Error), and let user try again.
Maybe I can use a lock in database, but most suggest don't do this, because of performance.
Maybe I can use Optimistic Concurrency Control, so I need write my code like this
public class RegisterCustomerCommandHandler : IRequestHandler<RegisterCustomerCommand, CustomerDto>
{
......
public async Task<CustomerDto> Handle(RegisterCustomerCommand request, CancellationToken cancellationToken)
{
var customer = new Customer(request.Email, request.Name, this._customerUniquenessChecker);
if (EmailAlreadyExist())
{
throw new EmailAlreadyExistException(request.Email);
}
await this._customerRepository.AddAsync(customer);
try
{
await this._unitOfWork.CommitAsync(cancellationToken);
}
catch(DbUpdateException)
{
//do something
}
return new CustomerDto { Id = customer.Id.Value };
}
}
But it may has many change in a database transaction, I can't know who throw the DbUpdateException.
If I has a situation, user can update same record, and it happen frequently, user may complain.
How to handle this gracefully.