我已经创建了一个OracleUnitOfWork和Repository类,如下所示:

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方法上出错并指出:


  你调用的对象是空的。


有时我什至会收到此错误:


  索引超出范围。必须为非负数并且小于集合的大小。
  参数名称:索引


但是我不知道如何解决这个问题。
在此之前,我遇到了GetRepository方法的问题,但是当我添加锁定后,此问题得以解决。我无法对ExecuteReaderAsync方法执行此操作,因为它将不再是异步的,而我需要这样做。

有谁知道如何解决这个问题?

最佳答案

KushanOracleConnection不是线程安全是正确的。如果确实需要同时执行多个查询并且不受开销的影响,则可以删除SingleInstance()并允许构建多个实例。

这样,您的每个线程都可以获取OracleUnitOfWork的新实例并独立完成其工作(获取数据,执行更改,保留更改)。

10-08 11:10