Readdy Write  
0,00 €
Your View Money
Views: Count
Self 20% 0
Your Content 60% 0

Users by Links 0
u1*(Content+Views) 10% 0
Follow-Follower 0
s2*(Income) 5% 0

Count
Followers 0
Login Register as User

Gelöst: fail: Microsoft.EntityFrameworkCore.Update[10000] An exception occurred in the database while saving changes

17.05.2018 (👁9076)

EF SQL command

 

 

Behebung des Fehlers

//*fail: Microsoft.EntityFrameworkCore.Update[10000]

//*An exception occurred in the database while saving changes for context type 'Readdy.Data.ApplicationDbContext'.

//*System.InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

 

Problem:

Wenn man einfache Daten auf einem SQL Server schreibt, welche einen schnellen Transfer benötigen ohne große Zwischenprüfung, dann führt das Arbeiten mit Entity Framework manchmal zu Problemen.

In einer Asp.Net Webserver Umgebung mit vielen gleichzeitigen Client-Zugriffen können Schnell die gleichen Datensätze während des Lesens schon wieder von anderen beschrieben werden.

Das Problem ist dabei, dass Entitiy Framework die Daten kurz als ADO Datensatz vom SQL Server auf den IIS Webserver lädt. In dieser Zeit zwischenzeit kann es zu veränderungen kommen, welche im Normalfall mit EF geprüft werden.

Das Prüfen wird mit EF-Tracking umgesetzt.

Für einfache Lese-Vorgänge kann das Daten-Tracking abgeschaltet werden und die Daten direkt mit SQL Execute Befehlen ausgeführt werden.

Einfache Lesen von Daten mit AsNoTracking()

Beispiel in Asp.Net Core 2 mit EF und Linq

//< with dbContext >

User_SumModel sumInfo = Database.EF_Model.dbContext.tbl_User_Sums.AsNoTracking().FirstOrDefault(u => u.IDUser == IDUser);

//</ with dbContext >

Direktes Schreiben auf dem SQL Server kann man mit ExecuteSqlCommand(sql) ausfühen

Database.EF_Model.dbContext.Database.ExecuteSqlCommand(sSQL);

 

Mein Beispiel-Code

Hier wird geprüft, ob ein Info-Datensatz vorhanden ist, wenn nicht, dann wird ein neuer Datensatz erstellt, ansonsten wird ein Zähler im Info-Datensatz erhöht.

Das Lesen des Datensatzes findet mit EF.AsNoTracking() readonly statt.

Die Schreib Vorgänge finden direkt auf den SQL Server statt.

    public static void increase_User_SumViews_Self_byIDUser(long IDUser)

    {

        //------------< increase_User_SumViews_Self(User) >------------

        try

        {

            //*get User Summary Table

            //< with dbContext >

            User_SumModel sumInfo = Database.EF_Model.dbContext.tbl_User_Sums.AsNoTracking().FirstOrDefault(u => u.IDUser == IDUser);

            //</ with dbContext >

            //--< Increment SumCounter >--

            string sSQL;

            if (sumInfo == null)

            {

                sSQL = "INSERT INTO [dbo].[tbl_User_Sums]";

                sSQL += Environment.NewLine + "([IDUser],[dtStart],[dtEnd],[SumViews_Self])";

                sSQL += Environment.NewLine + "VALUES(123, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1)";

            }

            else

            {

                sSQL = "UPDATE[dbo].[tbl_User_Sums]  SET [SumViews_Self] = ISNULL([SumViews_Self], 0)+1  ,[dtEnd]=CURRENT_TIMESTAMP   WHERE [IDUser]=" + IDUser;               

            }

            Database.EF_Model.dbContext.Database.ExecuteSqlCommand(sSQL);

            //--</ Increment SumCounter >--

        }

        finally

        { }

        //------------</ increase_User_SumViews_Self(User) >------------

    }

 

 

Im Debugger sieht man, dass der Update-Befehl nicht zu einer Erhöhung des zuvor gelesenen Zählerwertes im EF-Puffer führt.

Die Erhöhung des Wertes auf dem SQL-Server selbst hat aber schon statt gefunden. Der Wert wird aber nicht weiterverwendet und muss deshalb nicht getrackt werden.

 

 

 

Referenz:

Vormaliger Entity Framework Code, welcher zwar schon funktierte, aber wie oben beschrieben optimiert wurde

//*fail: Microsoft.EntityFrameworkCore.Update[10000]

    //*An exception occurred in the database while saving changes for context type 'Readdy.Data.ApplicationDbContext'.

    //*System.InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

    //public static await Task increase_User_SumViews_Self_byIDUser(long IDUser)

    public static void increase_User_SumViews_Self_byIDUser(long IDUser)

    {

        //------------< increase_User_SumViews_Self(User) >------------

        try

        {

            //*get User Summary Table

            User_SumModel summary = new User_SumModel();

            //< with dbContext >

            //summary = await Database.EF_Model.dbContext.tbl_User_Sums.FirstOrDefaultAsync(u => u.IDUser == IDUser);           

            summary = Database.EF_Model.dbContext.tbl_User_Sums.FirstOrDefault(u => u.IDUser == IDUser);

            //</ with dbContext >

            //--< Increment SumCounter >--

            if (summary == null)

            {

                summary = new User_SumModel();

                summary.SumViews_Self = 1;

                summary.IDUser = IDUser;

                summary.dtStart = DateTime.Now;

                summary.dtEnd = DateTime.Now;

                Database.EF_Model.dbContext.tbl_User_Sums.Add(summary);                

            }

            else

            {

                summary.SumViews_Self += 1;

                summary.dtEnd = DateTime.Now;

            }

            //await Database.EF_Model.dbContext.SaveChangesAsync(true);

            Database.EF_Model.dbContext.SaveChanges(true);

            //--</ Increment SumCounter >--

        }

        finally

        {

        }

        //------------</ increase_User_SumViews_Self(User) >------------

    }