Only allows 5 attempts at key duplication Entityframework? a 6 badge in Savechange()

Asked

Viewed 96 times

-1

Key duplication in write with Entityframework in competition with many users only allows 5 attempts and 6 badge in Savechange() (example 1).

If it is with Sqlconnection it does not give any problem (example 2).

Example 1:

Public Sub AdicionaAux(ByRef Tentativas As Integer)
        Dim inBlnRepete As Boolean = False
        Try
            Using ctx As New BD.Dinamica.Aplicacao
                Using trans As DbContextTransaction = ctx.Database.BeginTransaction(IsolationLevel.RepeatableRead)
                    Try
                        Dim t As tbArtigos = ctx.tbArtigos.FirstOrDefault
                        ctx.tbArtigos.Attach(t)
                        ctx.Entry(t).State = EntityState.Added
                        ctx.SaveChanges()
                    Catch ex As Exception
                        trans.Rollback()
                        Throw
                    End Try
                End Using
            End Using
        Catch
            inBlnRepete = True 'RepeteFuncaoPorConcorrencia(ex, Tentativas)
            If inBlnRepete And Tentativas < 5 Then
                AdicionaAux(Tentativas)
            Else
                Throw
            End If
        End Try
    End Sub

Example 2:

 Public Sub AdicionaAux(ByRef Tentativas As Integer)
        Try
            Dim appServidorSQL As String = ConfigurationManager.AppSettings("ServidorSQL")
            Dim appInstanciaSQL As String = ConfigurationManager.AppSettings("InstanciaSQL")
            Dim appBDEmpresa As String = ConfigurationManager.AppSettings("BDEmpresa")
            Using connection As New SqlConnection("Server=" & appServidorSQL & "\" & appInstanciaSQL & ";User ID=sa;Initial Catalog=" & appBDEmpresa & ";")
                connection.Open()
                Dim command As SqlCommand = connection.CreateCommand()
                Dim transaction As SqlTransaction
                transaction = connection.BeginTransaction("SampleTransaction")
                command.Connection = connection
                command.Transaction = transaction
                Try
                    command.CommandText = _
                      "Insert into tbDestinos (Codigo, Descricao, Ativo, Sistema, DataCriacao, UtilizadorCriacao) VALUES ('POR', 'Description',1,1,getdate(),'xxx')"
                    command.ExecuteNonQuery()
                    transaction.Commit()
                Catch ex As Exception
                    transaction.Rollback()
                    Throw
                End Try
            End Using
        Catch
            AdicionaAux(Tentativas)
        End Try
    End Sub

2 answers

1


The solution is here:

https://www.codeproject.com/Tips/758469/Implementing-Connection-Resiliency-with-Entity-Fra

public class PharylonExecutionStrategy : DbExecutionStrategy
    {
        /// <summary>
        /// The default retry limit is 5, which means that the total amount of time spent 
        /// between retries is 26 seconds plus the random factor.
        /// </summary>
        public PharylonExecutionStrategy()
        {
        }

        /// <summary>
        /// Creates a new instance of "PharylonExecutionStrategy" with the specified limits for
        /// number of retries and the delay between retries.
        /// </summary>
        /// <param name="maxRetryCount"> The maximum number of retry attempts. </param>
        /// <param name="maxDelay"> The maximum delay in milliseconds between retries. </param>
        public PharylonExecutionStrategy(int maxRetryCount, TimeSpan maxDelay)
            : base(maxRetryCount, maxDelay)
        {
        }

        protected override bool ShouldRetryOn(Exception ex)
        {
            bool retry = false;

            SqlException sqlException = ex as SqlException;
            if (sqlException != null)
            {
                int[] errorsToRetry =
                {
                    1205,  //Deadlock
                    -2,    //Timeout
                    2601  //primary key violation. Normally you wouldn't want to retry these, 
                          //but some procs in my database can cause it, because it's a crappy 
                          //legacy junkpile.
                };
                if (sqlException.Errors.Cast<SqlError>().Any(x => errorsToRetry.Contains(x.Number)))
                {
                    retry = true;
                }
                else
                {
                    //Add some error logging on this line for errors we aren't retrying.
                    //Make sure you record the Number property of sqlError. 
                    //If you see an error pop up that you want to retry, you can look in 
                    //your log and add that number to the list above.
                }
            }
            if (ex is TimeoutException)
            {
                retry = true;
            }
            return retry;
        }
    } 
  • 2

    Thanks for the answer. But the Shouldretryon function in accepts blocks with Transaction.

  • 1

    The solution is to use manual call execution strategy. example: https://msdn.microsoft.com/pt-pt/data/dn307226, however I have not yet been able to put to work.

0

The opening of the transactional scope is wrong. You do not open the scope from the connection to the database: open from the own . NET, because object handling is not a purely database issue.

The right thing would be something like this:

Public Sub AdicionaAux(ByRef Tentativas As Integer)
    Dim inBlnRepete As Boolean = False
    Try
        Using ctx As New BD.Dinamica.Aplicacao
            'Mude aqui
            Using trans As New TransactionScope(TransactionScopeAsyncFlowOption.Enabled)
                'Não use Try com contexto. Intercepte exceções nos eventos OnException 
                'do Controller ou SaveChanges e SaveChangesAsync do contexto.
                'Try
                Dim t As tbArtigos = ctx.tbArtigos.FirstOrDefault
                'Não entendi por que você está selecionando o registro e fazendo Attach nele.
                'Apenas a seleção já faz o registro ser observado, então estou comentando esta linha.
                'ctx.tbArtigos.Attach(t)
                ctx.Entry(t).State = EntityState.Added
                ctx.SaveChanges()
                trans.Complete()
                'Catch ex As Exception
                '    trans.Rollback()
                '    Throw
                'End Try
            End Using
        End Using
    'Considere retirar este Catch também
    Catch
        inBlnRepete = True 'RepeteFuncaoPorConcorrencia(ex, Tentativas)
        If inBlnRepete And Tentativas < 5 Then
            AdicionaAux(Tentativas)
        Else
            Throw
        End If
    End Try
End Sub

The Entity Framework is not a 100% dedicated SQL ORM, or a type of relational technology involving SQL: it is a framework agnostic. In addition, the support to multithreading is not safe natively: it takes an extra effort of the programmer for critical regions.

  • 1

    First of all, thank you very much for your reply. I removed Try, put the Transactionscope without being Async and put the attempts in the controller, however at the end of the 5 attempts the visual studio blocks. This only seems to happen with IX SQL single key errors, which is the case I need. 'Cause there could be multiple users with the same number recording and so on the go I would increase the number to +1.

  • Now that I’ve realized I forgot to complete the Scope. Try testing again.

  • 1

    I managed to get it to work without any Try and with the Onexception, but arrives to 6 failed attempt, badge in it. It will not be some parameterization of dbcontext?

  • I don’t understand what kind of test you are doing. Could you give an example by editing your question? In theory, this alone makes the code work the right way.

  • I found another problem in your code. You do not select the record and do a Attach of it soon after. The context already observes the record, then the Attach is useless and problematic at that point.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.