2016-12-07 51 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,要么重新考虑你的方法(即不要使用单身)。

请记住,如果使用不正确,锁定会导致多线程性能下降。