0

I have a command class as part of a CQRS workflow. It inserts into and updates a few different tables, like so:

public class ClassName : IBusinessCommand<CommandRequest, CommandResult>

private readonly DatabaseContext _dbContext;

public ClassName(DatabaseContext dbContext)
{
    _dbContext = dbContext;
}


public async Task<CommandResult> ExecuteAsync(CommandRequest request, CancellationToken token)
{
    var user = _dbContext.Users.NoTracking()
        .Where(x => x.request.UserId);

    if (user != null)
    {
        var class = new Class
        {
             ClassID = 1,
             UserID = user.UserID,
             CreateDate = _clock.UtcNow
        }

        _dbContext.Classes.Add(class);
    }

    var seat = await FunctionThatGetsSeatRecord(token);

    seat.ModifyDate = _clock.UtcNow;
    seat.SeatsUsed++;
    seat.SeatsRemaining--

    return CommandResult.WithSuccess();
}

We have a base class called CommandRunner (that is used for all of our commands like this). Within this base class, it calls the ExecuteAsync function (for whichever type parameter is passed in), as follows:

var command = _container.Resolve<IBusinessCommand<TData, TResult>>(
    new TypedParameter(typeof(IHoneyBeeDbContext), dbContext));
                    
var result = await command.ExecuteAsync(data, token);
await dbContext.SaveChangesAsync();
transaction.Commit();

so that all changes made to that commands dbContext are saved and committed as a single transaction. We've had this same infrastructure in place for our CQRS for a while now and it's worked perfectly fine. The class above was previously in a different service class and I decided to convert it to our CQRS flow, so I made it into a command. For reasons I'm not able to figure out (and I've been mulling over this for several hours), I can get the created class to add to the database properly, but I cannot get the seat record to update. I've debugged the entire class and followed and inspected the _dbContext variable throughout the entire flow and the updates appear correctly in there, but when it's saved and committed, it does not update the database for that one variable (but it does for the class). Does anyone see what could be wrong here?

  • Does FunctionThatGetsSeatRecord get the seat with no tracking? – Caius Jard Jun 14 '21 at 05:23
  • @CaiusJard I am using NoTracking() on those queries as well. – Ry.the.Stunner Jun 14 '21 at 05:38
  • Do not use NoTracking() if you want to modify, because the context needs to track the change and you prevent it with NoTracking() – Tunjay Jun 14 '21 at 05:41
  • @Tunjay thank you much!! I guess I should do a little more research into what the NoTracking() actually does, I was just modifying an existing query and I guess I didn't realize what that did. – Ry.the.Stunner Jun 14 '21 at 05:43
  • There's nothing obviously wrong in what you've posted. You'll need to debug more, and if you reduce your issue to a small, self-contained repro, post it in your question. – David Browne - Microsoft Jun 14 '21 at 12:33

1 Answers1

1

You indicated that the function that returns you a seat uses NoTracking() (typo of AsNoTracking() ?), which turns off change tracking, so EF was blind to changes you made to the seat.

Do not use "No Tracking" on collections you plan to query, update and save the entities within

(Using it in certain EF versions or situations may not bring any benefit at all, see https://github.com/dotnet/efcore/issues/14366)

Detail on non-tracked entities: What difference does .AsNoTracking() make?

Caius Jard
  • 72,509
  • 5
  • 49
  • 80
  • Thank you, I will keep that in mind. I wasn't entirely sure what AsNoTracking() did as I had just been retrofitting some existing queries. – Ry.the.Stunner Jun 14 '21 at 05:46