Reputation: 21
I have defined entity Application which is also root aggregate, it's children are Roles. I follow rule that you should only define repository for an aggregate roots and I've encountered problem when trying to insert new role into database.
The scenario in my application:
internal void AddRole(string name, string code, string description, bool isSystemic)
{
Roles.Add(RoleFactory.Create(name, code, description, isSystemic, Id));
}
This operation creates new Role entity and adds it into application role collection.
Now I want to call SaveChanges
on my repository and insert newly created entity, to do that I use the following code in my domain service:
public async Task AddRoleAsync(
Guid applicationId,
string name,
string code,
string description,
bool isSystemic,
CancellationToken cancellationToken)
{
var application = await _applicationRepository.GetAsync(applicationId, false, true, cancellationToken)
?? throw new Exception();
application.AddRole(name, code, description, isSystemic);
_applicationRepository.Update(application);
await _applicationRepository.SaveChangesAsync();
}
I get an error:
Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions
I've tried following this link but found none meaningful information for my case. I'm the only person using database so there's no chance for someone else editing / deleting data during my operation.
Under normal circumstances I would call AddAsync
in RoleRepository
but as I stated above I really don't want to create repositories for entities other than aggregate roots. I'm new to DDD so any help will be appreciated.
Edit: I decided to provide additional information about ApplicationRepository
. Here's the code:
public async Task SaveChangesAsync()
{
var potentiallyAffectedEntities = await DbContext.GetAffectedTrackedEntitiesAsync();
await DbContext.SaveChangesAsync(); // Exception occurs here
await DbContext.DispatchDomainEventsAsync(DomainEventPublisher, potentiallyAffectedEntities);
}
public void Update(TAggregateRoot aggregateRoot)
=> DbContext
.Set<TAggregateRoot>()
.Update(aggregateRoot);
For debug purposes I added this code to my repository:
var potentiallyAffectedEntities = await DbContext.GetAffectedTrackedEntitiesAsync();
potentiallyAffectedEntities.Skip(1)
.FirstOrDefault() // This gets new role entity
.State = EntityState.Added;
This code passes without any issues so I assume the problem is that EF sets EntityState
to Modified
instead of Added
. Question now is how I can force it do it the other way?
Upvotes: 0
Views: 523
Reputation: 21
Ok I finally found explanation, the issue was that I generated RoleId (Guid) in my code, so Entity's framework method update was marking it's entry as modified. Solution was to assign Guid.Empty instead and let database generate it. This solved the problem as the ef now properly sets entry state as Added. So the code inside my factory now looks like this:
internal static Entities.Role Create(string name, string code, string description, bool isSystemic, Guid applicationId)
=> new(
Guid.Empty,
name,
code,
description,
isSystemic,
applicationId);
Hopefully it helps someone :)
Upvotes: 1