none
Data is not updated in database if Entity Update happens in the moment of the SaveChanges. RRS feed

  • Question

  • Recently during the Automation tests, we got the situation when the updated field of the Entity is updated in the database. After investigation, we realized that it is related to the way we CRUD the entities and Save them.

    Overall we save the changes periodically (every 1 minute), while the changes happen all the time for 20+ different tables. These updates may happen on different threads. Save changes may happen in a different thread as well.

    So, the issue we got that Update of the entities happens when we do Save changes, at least we think it is the reason. 

    Following is the snippet of the code that I successfully reproduced this issue.

    using System; using System.Collections; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApp1 { public partial class Form1 : Form { private dbEntities _dbContext; private bool _runTime; private Grade grade; private object locker = new object();

    public Form1() { InitializeComponent(); } private void buttonStart_Click(object sender, EventArgs e) { _dbContext = new dbEntities(); _runTime = true; grade = new Grade() { GradeName = "TEST", Section = "TEST" }; _dbContext.Grades.Add(grade); _dbContext.SaveChanges(); Task.Run(() => { while (_runTime) { try { var stud = new Student { StudentName = "Bill" }; grade.Students.Add(stud); } catch (Exception exception) { Console.WriteLine(exception); } Thread.Sleep(100); } }); Task.Run(() => { while (_runTime) { try { var students = grade.Students.Where(s => s.Weight == 0).ToList(); foreach (var student in students) { student.Weight = 60; student.Height = 170; Console.WriteLine($"StudentId: {student.StudentID}; "); } } catch (Exception exception) { Console.WriteLine(exception); } Thread.Sleep(10); } }); Task.Run(() => { while (_runTime) { try { _dbContext.SaveChanges(); } catch (Exception exception) { Console.WriteLine(exception); } Thread.Sleep(10); } }); } private void buttonStop_Click(object sender, EventArgs e) { _runTime = false; Console.WriteLine("All Students"); foreach (var gradeStudent in grade.Students) { Console.WriteLine($"{gradeStudent.StudentID}-{gradeStudent.Weight}"); } } } }


    I am using here ADO.NET Entity data model and Windows Forms.

    As you see I have 3 threads here. If I use lock or SemaphoreSlim the issue can be resolved easily. However, for the real application it may cause other issues, because we have a lot of different logic which does Add and Update, while only 1 location when we save the data.

    So, the question is whether it is possible to resolve this issue using only Entity Framework tools.




    Friday, January 24, 2020 2:09 PM

All replies

  • So, the question is whether it is possible to resolve this issue using only Entity Framework tools.

    IMO, this is a design issue and not an EF issue where you need to communicate across threads that a save is happening and suspend any update until the save is complete. The save needs to happen in the heartbeat thread, the thread the program started up on,  and not doing saves on spawned threads.


    • Edited by DA924x Friday, January 24, 2020 8:56 PM
    Friday, January 24, 2020 8:55 PM
  • Hi Pavel Brokhman,

    According to your description, I think it is a entity framework concurrency conflicts issue, optimistic concurrency involves optimistically attempting to save your entity to the database in the hope that the data there has not changed since the entity was loaded. If it turns out that the data has changed then an exception is thrown and you must resolve the conflict before attempting to save again. 

    The following document provide 4 ways to handle these kinds of situation by catching DbUpdateConcurrencyException.

    https://docs.microsoft.com/en-us/ef/ef6/saving/concurrency

    Best regards,

    Zhanglong


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Monday, January 27, 2020 3:01 AM
    Moderator
  • You know, it would be nice if we were getting this exception. It would help us a lot to handle the issue. Unfortunatly, it is not the case, even my example is not executing this exception. Meanwhile I consider next option.

    …
    CollectUpdatedTrackedEntities(_dbContext.ChangeTracker);
    _dbContext.SaveChanges();
    MarkAsModifiedNotUpdatedTrackedEntities(_dbContext.ChangeTracker);
    ….
    
    
    
            private void MarkAsModifiedNotUpdatedTrackedEntities(DbChangeTracker changeTracker)
            {
                var entries = changeTracker.Entries();
                foreach (var entry in collectedUpdatedStudents.Where(e => e.State != EntityState.Added))
                {
                    var databaseValues = entry.GetDatabaseValues();
                    foreach (var propertyName in entry.CurrentValues.PropertyNames)
                    {
                        var currentValue = entry.CurrentValues[propertyName];
                        var databaseValue = databaseValues[propertyName];
    
                        if (currentValue == null || databaseValue == null)
                            continue;
    
                        var valuesEqual = databaseValue.Equals(currentValue);
                        if (!valuesEqual)
                        {
                            Console.WriteLine($"StudentId: {entry.CurrentValues["StudentID"]}; Entry State - {entry.State}");
                            entry.State = EntityState.Modified;
                        }
                    }
                }
            }
    
            private void CollectUpdatedTrackedEntities(DbChangeTracker changeTracker)
            {
                var entries = changeTracker.Entries();
                foreach (var entry in entries.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified))
                {
                    collectedUpdatedStudents.Add(entry);
                }
            }

    Monday, January 27, 2020 2:05 PM
  • Hi Pavel Brokhman,

    I am glad to know that you found a workaround to resolve the issue, please mark it as answer, it will be beneficial to other communities who have the similar issue.

    If you have any other, welcome to post here. 

    Best regards,

    Zhanglong


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, January 28, 2020 2:06 AM
    Moderator