简体   繁体   English

ExecuteReaderAsync和Autofac

[英]ExecuteReaderAsync and Autofac

I have created an OracleUnitOfWork and Repository class like this: 我创建了一个像这样的OracleUnitOfWorkRepository类:

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);
            }
        }
    }
}

In my AutoFac module, I register the OracleUnitOfWork as a single instance like this: 在我的AutoFac模块中,我将OracleUnitOfWork注册为单个实例,如下所示:

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

For most queries, this is fine, but I seem to have a problem when trying to execute multiple queries simultaneously. 对于大多数查询,这很好,但我似乎在尝试同时执行多个查询时遇到问题。 It errors out on the ExecuteReaderAsync method in my repository and states: 它在我的存储库中的ExecuteReaderAsync方法上出错并指出:

Object reference not set to an instance of an object. 你调用的对象是空的。

Sometimes I even get this error: 有时我甚至会收到此错误:

Index was out of range. 指数超出范围。 Must be non-negative and less than the size of the collection. 必须是非负数且小于集合的大小。 Parameter name: index 参数名称:index

But I can't figure out how to fix the issue. 但我无法弄清楚如何解决这个问题。 Prior to this I was getting issues with the GetRepository method, but when I added locking that fixed the issue. 在此之前,我遇到了GetRepository方法的问题,但是当我添加锁定时修复了问题。 I can't do that to ExecuteReaderAsync method because it will no longer be asynchronous and I need it to be. 我不能这样做ExecuteReaderAsync方法,因为它将不再是异步的,我需要它。

Does anyone know how to solve this issue? 有谁知道如何解决这个问题?

For most queries, this is fine, but I seem to have a problem when trying to execute multiple queries simultaneously. 对于大多数查询,这很好,但我似乎在尝试同时执行多个查询时遇到问题。

You have a race condition, you're trying to access the same references across multiple threads and getting "spooky" behaviour. 你有一个竞争条件,你试图跨多个线程访问相同的引用,并获得“怪异”的行为。

You can't pass a mutable singleton across multiple threads like that, it will break. 你不能在这样的多个线程中传递一个可变单例,它会破坏。 Either bite the bullet and use aa _lock or rethink your approach (ie Don't use a singleton). 咬住子弹并使用a_lock或重新考虑你的方法(即不要使用单身人士)。

Just remember, locks will kill your multi-threaded performance if not used correctly. 请记住,如果使用不正确,锁将会破坏您的多线程性能。

Kushan is right about OracleConnection not being thread safe. KushanOracleConnection不是线程安全是正确的。 If you really need to execute multiple queries in the same time and you are not affected by the overhead, you can remove SingleInstance() and allow multiple instances to be built. 如果您确实需要在同一时间执行多个查询并且不受开销影响,则可以删除SingleInstance()并允许构建多个实例。

This way, each of your threads can obtain a new instance of OracleUnitOfWork and do its work independently (fetch data, perform changes, persist changes). 这样,每个线程都可以获取OracleUnitOfWork的新实例并独立完成其工作(获取数据,执行更改,持久更改)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM