Social Icons

twitter google plus linkedin rss feed

Pages

22.4.19

Concurrency Errors and Value Objects in Entity Framework 2.x

We have been using value objects in our databases for a couple of months and after we passed the first hurdles with entity framework we have noticed improvements in speed and a significant decrease in the Includes wich is all what we wanted to get... but suddenly...

In one of the microservices we noticed we were getting concurrency exceptions every time we updated a value object. And it wasn't a complicated nested value object, it was a value object made of two guids and two strings.

I spent a good afternoon trying to figure out why were we getting the error and could not see anything wrong in the code... and I thought... it's entity framework again!

I asked for help to the rest of the team and they found this thread Changes on Owned Entites Properties causes a concurrency conflict on same dbContext where they propose a workaround. We tried and it didn't work but we thought it was going in the right direction so we debug it and change it a bit and voilà the rowversion column was being updated again both in SQL and in my backend!

The modified code is this:
private void ConcurrencyFix()
{
    var changedEntriesWithVos = ChangeTracker.Entries().Where(e =>
        e.State == EntityState.Unchanged
        && e.References.Any(r =>
            r.TargetEntry != null
            && (r.TargetEntry.State == EntityState.Modified || r.TargetEntry.State == EntityState.Added)
            && r.TargetEntry.Metadata.IsOwned()
            && e.Metadata.Relational().TableName == r.TargetEntry.Metadata.Relational().TableName)).ToArray();

    foreach (var entry in changedEntriesWithVos)
        entry.State = EntityState.Modified;
}

And we have placed it in our SaveChanges methods in our base context, from which all of our contexts are inheriting so we are sure this code is always being executed when we save.
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
    ConcurrencyFix();
    return base.SaveChanges(acceptAllChangesOnSuccess);
}

public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
    ConcurrencyFix();
    return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}

Have fun everyone!

No comments:

Post a Comment

23.3.19

Configuring and Updating nested value objects in Entity Framework

Wow three years since the last post... not bad!

For those of you who are curious I have finally left SharePoint and now I am sure we both are happier. But with a new platform come new issues.

I am working now in a web application with .net core and entity framework and we have started using ValueObjects. All was fun and games until last wednesday when I decided to make one value object a child of another.

We have User:
public class User: ValueObject
{
    public string ActiveDirectorySID { get; private set; }
    public string Email { get; private set; }
    public string Name { get; private set; }

    (...)
}

We have Position:
public class Position : ValueObject
{
    public Guid PositionId { get; private set; }
    public string JobTitle { get; private set; }
    public User Employee { get; private set; }
    (...)
}

And Finally we have CompanyPolicy:
public class CompanyPolicy : Entity
{
    public string Title { get; private set; }
    (...)
    public Position Owner { get; private set; }
    public Position Reviewer { get; private set; }
    public Position Validator { get; private set; }
    (...)
}

As you can see CompanyPolicy owns three Positions and each position owns an User. The first issue we need to deal with is the mappings of the CompanyPolicy object. After reading half of the internet the solution ended up being easy, the configuration code for the Positions inside a CompanyPolicy is:
public override void Configure(EntityTypeBuilder<CompanyPolicy> builder)
{
  base.Configure(builder);

  builder.Property(x => x.Title);
  (...)
  builder.OwnsOne(p => p.Owner, cb => { cb.OwnsOne(e => e.Employee); });
  builder.OwnsOne(p => p.Reviewer, cb => { cb.OwnsOne(e => e.Employee); });
  builder.OwnsOne(p => p.Validator, cb => { cb.OwnsOne(e => e.Employee); });
  (...)
}

So far so good, we add the migration an it does not fail, we update the databases and it works... we add a value and all the fields get populated... We did it?! NOPE. The values never update. No matter what you do.

We read the other half of the internet and found this thread where they show a workaround and say this is already solved in the 3.0 preview, and as we don't want to wait and we don't want to install VS2019 we decided to go for the workaround.

Based on it and after a couple of iterations we came up with this method:
public static void UpdateChildValueObjects<TEntity, TParent>(
    this BaseAllensContext context,
    TEntity rootEntity,
    Expression<Func<TParent, ValueObject>> getChildValueObject,
    params Expression<Func<TEntity,TParent>>[] getParentValueObjectArray)
        where TEntity : class where TParent:class
        {
    var values = new List<ValueObject>();

    //In the DetectChanges and when setting the values the rootEntity gets
    //the values from the database. That's why we need to save them before!
    for (int i = 0; i < getParentValueObjectArray.Length; i++)
    {
        values.Add(getChildValueObject.Compile()
          .Invoke(getParentValueObjectArray[i].Compile().Invoke(rootEntity)));
    }

    context.ChangeTracker.DetectChanges();

    for (var i = 0; i < getParentValueObjectArray.Length; i++)
    {
        context.Entry(rootEntity).Reference(getParentValueObjectArray[i])
          .TargetEntry.Reference(getChildValueObject)
          .CurrentValue = values[i];
    }

    context.Entry(rootEntity).State = EntityState.Modified;
}

Ugly anyone? Hard to read? It is not ugly, I like it. And it sits in your repository beautifully.

In the repository we have added this method:

public void Update(CompanyPolicy policy)
{
    Context.UpdateChildValueObjects(policy, x => x.Employee,
        x => x.Owner, x => x.Validator, x => x.Reviewer);
}

This is what I wanted to show :)
So with this method in the repository you are telling that you want to update the Employee ValueObject in the Owner, Validator and Reviewer ValueObjects of the Entity policy. And you might be thinking... That looks nice but, how do you use it?
Well this is the ugly part...

First after we update the CompanyPolicy entity as we would normally do we need to make sure these value objects are updated so... I call the update first on the entity and then on the repository, like this:
var owner= companyPolicyDTO.Owner != null
    ? Mapper.Map<Position>(companyPolicyDTO.Owner)
    : Position.Empty;
var reviewer = companyPolicyDTO.Reviewer != null
    ? Mapper.Map<Position>(companyPolicyDTO.Reviewer)
    : Position.Empty;
var validator = companyPolicyDTO.Validator != null
    ? Mapper.Map<Position>(companyPolicyDTO.Validator)
    : Position.Empty;

companyPolicy.Update(companyPolicyDTO.Title, owner, reviewer, validator);
companyPolicyRepository.Update(companyPolicy);

And this is it. First the standard update then our workaround.

Known Issues:
- The method UpdateChildValueObjects sets the values of the non updated fields of the entity to what they where in the database. This means you can't call this method twice in the same transaction because the second time it will not update the values!

public void Update(CompanyPolicy policy)
{
    Context.UpdateChildValueObjects(policy, x => x.Employee,
        x => x.Owner, x => x.Validator, x => x.Reviewer);
}
OK

public void Update(CompanyPolicy policy)
{
    Context.UpdateChildValueObjects(policy, x => x.Employee,
        x => x.Owner);
    Context.UpdateChildValueObjects(policy, x => x.Employee,
        x => x.Reviewer);
    Context.UpdateChildValueObjects(policy, x => x.Employee,
        x => x.Validator);
}
Only the owner will be updated!

I hope this helps someone!

1 comment:

Post a Comment