我已经创建了一个 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 方法,因为它不再是异步的,我需要它。
有谁知道如何解决这个问题?
答案 0 :(得分:2)
对于大多数查询,这很好,但我似乎有问题 试图同时执行多个查询。
你有竞争条件,你试图跨多个线程访问相同的引用并获得“怪异”的行为。
你不能在这样的多个线程中传递可变单例,它会破坏。咬住子弹并使用_lock或重新考虑你的方法(即不要使用单身人士)。
请记住,如果使用不正确,锁会破坏你的多线程性能。
答案 1 :(得分:0)
Kushan
关于OracleConnection
不是线程安全的,这是正确的。如果您确实需要同时执行多个查询并且不受开销的影响,则可以删除SingleInstance()
并允许构建多个实例。
这样,每个线程都可以获得OracleUnitOfWork
的新实例,并独立完成工作(获取数据,执行更改,持续更改)。