2015-11-03 53 views
3

我試圖使用DBExecutionStrategy重試已超時的查詢,但是當超時發生時,我收到錯誤「The SqlParameter is已被另一個SqlParameterCollection包含「。我正在使用EF6。EF6重試過程拋出「SqlParameter已被另一個SqlParameterCollection包含」用於SqlQuery命令

我的查詢:

using (var ctx = new EntityModel()) 
{ 
    IEnumerable<ItemResponse> items= ctx.Database.SqlQuery<ItemResponse>(
      "spItemListGet @UserID", new SqlParameter("@UserID", UserID) 
    ).ToList(); 
} 

我的執行策略:

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

    SqlException sqlException = ex as SqlException; 
    if (sqlException != null) 
    { 
     int[] errorsToRetry = 
     { 
      -2,  //Timeout 
     }; 
     if (sqlException.Errors.Cast<SqlError>().Any(x => errorsToRetry.Contains(x.Number))) 
     { 
      retry = true; 
     } 
     else 
     { 
      throw ex; //dont retry 
     } 
    } 

    return retry; 
} 

堆棧跟蹤:

System.ArgumentException: The SqlParameter is already contained by another SqlParameterCollection. 
    at System.Data.SqlClient.SqlParameterCollection.Validate(Int32 index, Object value) 
    at System.Data.SqlClient.SqlParameterCollection.AddRange(Array values) 
    at System.Data.Entity.Core.Objects.ObjectContext.CreateStoreCommand(String commandText, Object[] parameters) 
    at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQueryInternal[TElement](String commandText, String entitySetName, ExecutionOptions executionOptions, Object[] parameters) 
    at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass65`1.<ExecuteStoreQueryReliably>b__64() 
    at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess) 
    at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass65`1.<ExecuteStoreQueryReliably>b__63() 
    at System.Data.Entity.Infrastructure.DbExecutionStrategy.Execute[TResult](Func`1 operation) 
    at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQueryReliably[TElement](String commandText, String entitySetName, ExecutionOptions executionOptions, Object[] parameters) 
    at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQuery[TElement](String commandText, ExecutionOptions executionOptions, Object[] parameters) 
    at System.Data.Entity.Internal.InternalContext.<>c__DisplayClass14`1.<ExecuteSqlQuery>b__13() 
    at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext() 
    at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 
    at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) 
    at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) 

什麼能不能做到防止這種錯誤? Database.SqlQuery可以用於執行策略嗎?

+0

執行策略工作正常,當我嘗試ctx.SaveChanges()。有任何想法嗎? –

回答

3

簡短回答:不,你不能這樣做(如果你的命令有參數)。

長的回答: 這是一個最小的問題重現。我從圖片中刪除了執行策略,並用循環代替僞造。該邏輯在ObjectContext中實現,特別是在ExecuteStoreQueryInternalAsync方法中。問題似乎是清除部分缺少一個command.Parameters.Clear()調用。

static void Main(string[] args) 
{ 
    TestQuery(); 
} 

private static void TestQuery() 
{ 
    using (var ctx = new ProductContext()) 
    { 
     var parameter = new SqlParameter("@ID", 1); 
     var commandText = "select * from product where ProductId = @ID"; 

     Action a =() => 
     { 
      IDbCommand command = new SqlCommand(); 
      command.CommandText = commandText; 
      command.Parameters.Add(parameter); 

      command.Connection = ctx.Database.Connection; 
      if (command.Connection.State != ConnectionState.Open) 
      { 
       command.Connection.Open(); 
      } 

      var reader = command.ExecuteReader(); 
      try 
      { 
       throw new Exception(); 
       while (reader.Read()) 
       { 
        var pId = reader["ProductID"]; 
       } 
       reader.Close(); 
      } 
      catch (Exception exc) 
      { 
       //for simplification, we just swallow this error, but in reality the connection error 
       //would reach the IDbExecutionStrategy, and would do a retry. Instead we fake the retry 
       //with a loop below 
      } 
      finally 
      { 
       reader.Dispose(); 

       //command.Parameters.Clear(); <--------- THIS LINE IS MISSING FROM EF 
       command.Dispose(); 
      } 
     }; 

     for (int i = 0; i < 2; i++) // we fake the retry with a loop now 
     { 
      a(); 
     } 
    } 
} 
+2

有關EF6錯誤的更多信息,請訪問:https://entityframework.codeplex.com/SourceControl/changeset/107283972666babbff10fb7409298f39200acb09 –

+0

嗯,很好。 11天前修復。 – Tamas

+0

@Tamas EF6.1.3中似乎沒有解決這個問題。如果用戶在DbExecutionStrategy中設置了重試,並在其代碼中調用了dbContext.Database.ExecuteSqlCommand,則它們將運行到上面列出的Sql Parameter異常。 – goroth

相關問題