I'm using MVC 5.* + AngularJS version, and came across a weird issue when trying to use eventbus.
When I try to use EventBus to do insert/update for a batch of UserOrganizationUnit records:
EventBus.Trigger(new UserOrganizationUnitsUpdateDto { UserId = user.Id, OrganizationUnitIds = input.GrantedOrganizationUnits });
It throws me an error:
$exception {"The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: Saving or accepting changes failed because more than one entity of type 'Abp.Authorization.Users.UserOrganizationUnit' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration."} System.InvalidOperationException
It works properly if I simply use:
updateList.ForEach(x => _userOrganizationUnitRepository.InsertOrUpdateAsync(x));
But updateList.ForEach(x => _userOrganizationUnitRepository.InsertOrUpdateAsync(x)); would take around 1-2 minutes for 9000 records... So I'm still prefer using eventbus to improve user experience.
The Id column in UserOrganizationUnit is already an identity column in database, but it is not decorated with DatabaseGeneratedOption. Is there a way to work around this issue?
3 Answer(s)
-
0
Afak if you make a database transaction in a different thread, db context can be disposed whenever the request ends.
Try to make a new scope in the background thread to use the db context there. You're introducing a race condition since the original db context might be disposed before your task runs.
-
0
<cite>alper: </cite> Afak if you make a database transaction in a different thread, db context can be disposed whenever the request ends.
Try to make a new scope in the background thread to use the db context there. You're introducing a race condition since the original db context might be disposed before your task runs.
Thank you very much for the quick reply. Yeah, I was thinking of that possible reason so I directly pass UserId and the OrgUnitId to eventBus, and in there I initialize a new UserManager to update/insert records..
EventBus.Trigger(new UserOrganizationUnitsUpdateDto { UserId = user.Id, OrganizationUnitIds = input.GrantedOrganizationUnits });
Would doing that still have the race condition issue?
-
0
hi,
In the below code you don't use await this is the problem.
updateList.ForEach(x => _userOrganizationUnitRepository.InsertOrUpdateAsync(x));
you need to add awaithere
updateList.ForEach(x => await _userOrganizationUnitRepository.InsertOrUpdateAsync(x));
What I can suggest you, do your database operations in the below using block. Otherwise when the request completes, the active unit of work will be disposed and you cannot reach DbContext anymore. If you start a new UnitOfWork with RequiresNew option you'll a have an isolated new UnitOfWork apart from the main one.
using (var uow = _unitOfWorkManager.Begin(new UnitOfWorkOptions { Scope = TransactionScopeOption.RequiresNew })) { // ... await uow.CompleteAsync(); }