ExecuteReaderAsync和Autofac

时间:2016-12-07 15:04:40

标签: c# oracle autofac

我已经创建了一个 OracleUnitOfWork 存储库类:

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

  

对象引用未设置为对象的实例。

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

  

指数超出范围。必须是非负数且小于集合的大小。   参数名称:index

但我无法弄清楚如何解决这个问题。 在此之前,我遇到了 GetRepository 方法的问题,但是当我添加锁定时修复了问题。我不能这样做 ExecuteReaderAsync 方法,因为它不再是异步的,我需要它。

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

2 个答案:

答案 0 :(得分:2)

  

对于大多数查询,这很好,但我似乎有问题   试图同时执行多个查询。

你有竞争条件,你试图跨多个线程访问相同的引用并获得“怪异”的行为。

你不能在这样的多个线程中传递可变单例,它会破坏。咬住子弹并使用_lock或重新考虑你的方法(即不要使用单身人士)。

请记住,如果使用不正确,锁会破坏你的多线程性能。

答案 1 :(得分:0)

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

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