2016-12-07 92 views
3

我已經創建了一個OracleUnitOfWork類是這樣的:ExecuteReaderAsync和Autofac

public class OracleUnitOfWork : IOracleUnitOfWork 
{ 
    // Private properties 
    private readonly OracleConnection _connection; 
    private readonly OracleCommand _command; 
    private readonly Dictionary<Type, object> _repositories; 
    private Object thisLock = new Object(); 

    /// <summary> 
    /// Default constructor 
    /// </summary> 
    /// <param name="config">The Cormar config class</param> 
    public OracleUnitOfWork(CormarConfig config) 
    { 

     // Create instances for our private properties 
     this._repositories = new Dictionary<Type, object>(); 
     this._connection = new OracleConnection(config.ConnectionString); 
     this._command = new OracleCommand 
     { 
      Connection = this._connection, 
      CommandType = CommandType.StoredProcedure, 
      BindByName = true 
     }; 

     // Open our connection 
     this._connection.Open(); 
    } 

    /// <summary> 
    /// Gets the entity repository 
    /// </summary> 
    /// <typeparam name="T">The entity model</typeparam> 
    /// <returns></returns> 
    public IRepository<T> GetRepository<T>() where T : class, new() 
    { 

     // Lock the thread so we can't execute at the same time 
     lock (thisLock) 
     { 

      // If our repositories have a matching repository, return it 
      if (_repositories.Keys.Contains(typeof(T))) 
       return _repositories[typeof(T)] as IRepository<T>; 

      // Create a new repository for our entity 
      var repository = new Repository<T>(this._command); 

      // Add to our list of repositories 
      _repositories.Add(typeof(T), repository); 

      // Return our repository 
      return repository; 
     } 
    } 

    /// <summary> 
    /// Dispose 
    /// </summary> 
    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    /// <summary> 
    /// Disposes of any attached resources 
    /// </summary> 
    /// <param name="disposing">A boolean indicating whether the object is being disposed</param> 
    protected virtual void Dispose(bool disposing) 
    { 

     // If we are disposing 
     if (disposing) 
     { 

      // Close our connection 
      this._connection.Close(); 
      this._connection.Dispose(); 
      this._command.Dispose(); 
     } 
    } 
} 

public class Repository<T> : IRepository<T> where T : class, new() 
{ 
    // private properties 
    private readonly OracleCommand _command; 
    private Object thisLock = new Object(); 

    /// <summary> 
    /// Default constructor 
    /// </summary> 
    /// <param name="command"></param> 
    public Repository(OracleCommand command) 
    { 
     this._command = command; 
    } 

    /// <summary> 
    /// Returns the datareader for the stored procedure 
    /// </summary> 
    /// <param name="storedProcedureName">The name of the SPROC to execute</param> 
    /// <param name="parameters">The parameters needed for the SPROC</param> 
    /// <returns></returns> 
    public async Task<IDataReader> ExecuteReaderAsync(string storedProcedureName, IList<OracleParameter> parameters = null) 
    { 

     // Set up our command 
     this.InitiateCommand(storedProcedureName, parameters.ToArray()); 

     // Return our data reader 
     return await this._command.ExecuteReaderAsync();    
    } 

    /// <summary> 
    /// Create, updates or deletes an entity 
    /// </summary> 
    /// <param name="storedProcedureName">The name of the SPROC to execute</param> 
    /// <param name="parameters">The parameters needed for the SPROC</param> 
    public async Task CreateUpdateOrDeleteAsync(string storedProcedureName, IList<OracleParameter> parameters = null) 
    { 

     // Set up our command 
     this.InitiateCommand(storedProcedureName, parameters.ToArray()); 

     // Execute our command 
     await this._command.ExecuteNonQueryAsync(); 
    } 

    /// <summary> 
    /// Intiates the command object 
    /// </summary> 
    /// <param name="storedProcedureName">The name of the SPROC to execute</param> 
    /// <param name="parameters">An array of parameters</param> 
    private void InitiateCommand(string storedProcedureName, OracleParameter[] parameters) 
    { 

     // Lock the thread so we can't execute at the same time 
     lock (thisLock) 
     { 

      // Set up the command object 
      this._command.CommandTimeout = 1800; 
      this._command.FetchSize = 1000; 
      this._command.CommandText = storedProcedureName; 
      this._command.Parameters.Clear(); 

      // If we have any parameters 
      if (parameters != null) 
      { 

       // Assign each parameter to the command object 
       this._command.Parameters.AddRange(parameters); 
      } 
     } 
    } 
} 

在我AutoFac模塊,我註冊OracleUnitOfWork作爲單個實例是這樣的:

builder.RegisterType<OracleUnitOfWork>().As<IOracleUnitOfWork>().SingleInstance(); 

對於大多數查詢,這是好的,但我似乎有一個問題,當試圖同時執行多個查詢。它的錯誤出在ExecuteReaderAsync方法在我的倉庫,並指出:

Object reference not set to an instance of an object.

有時我甚至得到這個錯誤:

Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index

但我無法弄清楚如何解決這個問題。 在此之前,我得到的問題與GetRepository方法,但是當我添加鎖定,解決了問題。我不能這樣做到ExecuteReaderAsync方法,因爲它不再是異步的,我需要它。

有誰知道如何解決這個問題?

回答

0

Kushan是正確的關於OracleConnection不是線程安全的。如果您確實需要在同一時間執行多個查詢,並且不受開銷的影響,則可以刪除SingleInstance()並允許構建多個實例。

這樣,每個線程都可以獲得OracleUnitOfWork的新實例並獨立完成其工作(獲取數據,執行更改,保持更改)。

2

For most queries, this is fine, but I seem to have a problem when trying to execute multiple queries simultaneously.

你有一個競爭條件,你試圖訪問跨多個線程相同的引用,並獲得「幽靈般的」行爲。

你不能像這樣在多線程中傳遞可變單例,它會中斷。要麼咬住子彈並使用a _lock,要麼重新考慮你的方法(即不要使用單身)。

請記住,如果使用不正確,鎖定會導致多線程性能下降。