saveChangesAsync not working on partial records

44 Views Asked by At

In a webservice I have a table that includes a 'Seq' field: it's a counter and for the sake of my application it must be filled with a progression of integer values, with no gaps. It may happen that this sequence is "broken" (for example if a record is deleted or other situations). I have then a method like the one below that renumbers the 'Seq' field. I have tested it (also in many other tables) and it works perfectly.

[HttpPut("RenumberSeq")]

        public async Task<IActionResult> RenumberSeq()
        {

            var orderedEntities = 
                await _context._UT_Risorse
                .OrderBy(r => r.Seq)
                .ToListAsync();

            var newSeq = 1;

            foreach (var obj in orderedEntities)
            {
                obj.Seq = newSeq;

                newSeq++;
            }

            await _context.SaveChangesAsync();

            return NoContent();
        }

The problem is that this table (differently from other tables where I used the same method) has a field named 'base64' that contains extremely long strings: it is in fact a field used to store files in the base64 format.

Being this field so heavy the method works but it's very slow: it takes even 30 sec to renumber 15 records, each record containing 30 to 50 kb strings... I then thought it could be a good idea to re-write the query like:

var orderedEntities =
            await (from r in _context._UT_Risorse
                   select new _UT_Risorsa
                   {
                       ID = r.ID,
                       NomeFile = r.NomeFile, //this is a non relevant field
                       TipoFile = r.TipoFile, //this is a non relevant field
                       Seq = r.Seq,  //this is the field to renumber
                   })
            .OrderBy(r => r.Seq)
            .ToListAsync();

        await _context.SaveChangesAsync();

        return NoContent();

so including all fields I need but SKIPPING the 'base64' heavy field. the problem is that if I write it like this the await _context.SaveChangesAsync() is not working. I get no error but the db table shows no change.

It's like if SaveChangesAsync required to work on the whole record and couldn't work on a partial one...is that a correct behaviour of this method? is there anything I can do in order to preserve my partial query (so avoiding to include the 'base64' field to keep it quick) and make the save work?

1

There are 1 best solutions below

0
Nicola On

As Eldar suggested I found it is actually as he says: the supposed behaviour of the EF is to edit and save the whole record. You must specify a different behavior. This is how I managed to solve

var orderedEntities =
    await (from r in _context._UT_Risorse
            select new _UT_Risorsa
            {
                ID = r.ID,
                Seq = r.Seq,
            })
    .OrderBy(r => r.Seq)
    .ToListAsync();

var newSeq = 1;
foreach (var obj in orderedEntities)
{
    //procedure to edit only part of the record
    var entityToUpdate = new _UT_Risorsa { ID = obj.ID, Seq = newSeq };
    _context.Attach(entityToUpdate);
    _context.Entry(entityToUpdate).Property(r => r.Seq).IsModified = true;

    newSeq++;
}
await _context.SaveChangesAsync();

return NoContent();

Now it's perfect: very fast and effective. Here the link where I got the answer https://kontext.tech/article/638/entityframework-core-update-only-one-field