324 lines
13 KiB
C#
324 lines
13 KiB
C#
/*
|
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
|
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
|
|
* All rights reserved.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace DynamORM.Tests.Helpers
|
|
{
|
|
internal sealed class FakeProviderState
|
|
{
|
|
public int CreatedConnections;
|
|
public int OpenCalls;
|
|
public int CloseCalls;
|
|
public int DisposeCalls;
|
|
public int BeginTransactionCalls;
|
|
public int ExecuteNonQueryCalls;
|
|
public int MaxConcurrentExecutions;
|
|
public int CurrentConcurrentExecutions;
|
|
public object LastTransactionSeen;
|
|
public readonly List<object> SeenTransactions = new List<object>();
|
|
public readonly List<FakeProviderConnection> Connections = new List<FakeProviderConnection>();
|
|
public ManualResetEventSlim FirstExecutionEntered = new ManualResetEventSlim(false);
|
|
public ManualResetEventSlim AllowExecution = new ManualResetEventSlim(true);
|
|
public bool BlockFirstExecution;
|
|
public bool BlockFirstReader;
|
|
public ManualResetEventSlim FirstReaderEntered = new ManualResetEventSlim(false);
|
|
public ManualResetEventSlim AllowReader = new ManualResetEventSlim(true);
|
|
private int _blocked;
|
|
private int _readerBlocked;
|
|
|
|
public void RecordExecution(IDbTransaction transaction)
|
|
{
|
|
int current = Interlocked.Increment(ref CurrentConcurrentExecutions);
|
|
int snapshot;
|
|
while ((snapshot = MaxConcurrentExecutions) < current)
|
|
Interlocked.CompareExchange(ref MaxConcurrentExecutions, current, snapshot);
|
|
|
|
Interlocked.Increment(ref ExecuteNonQueryCalls);
|
|
if (transaction != null)
|
|
LastTransactionSeen = transaction;
|
|
lock (SeenTransactions)
|
|
SeenTransactions.Add(transaction);
|
|
|
|
if (BlockFirstExecution && Interlocked.CompareExchange(ref _blocked, 1, 0) == 0)
|
|
{
|
|
FirstExecutionEntered.Set();
|
|
AllowExecution.Wait(TimeSpan.FromSeconds(5));
|
|
}
|
|
}
|
|
|
|
public void ExitExecution()
|
|
{
|
|
Interlocked.Decrement(ref CurrentConcurrentExecutions);
|
|
}
|
|
|
|
public void RecordReaderOpen(IDbTransaction transaction)
|
|
{
|
|
int current = Interlocked.Increment(ref CurrentConcurrentExecutions);
|
|
int snapshot;
|
|
while ((snapshot = MaxConcurrentExecutions) < current)
|
|
Interlocked.CompareExchange(ref MaxConcurrentExecutions, current, snapshot);
|
|
|
|
if (transaction != null)
|
|
LastTransactionSeen = transaction;
|
|
lock (SeenTransactions)
|
|
SeenTransactions.Add(transaction);
|
|
|
|
if (BlockFirstReader && Interlocked.CompareExchange(ref _readerBlocked, 1, 0) == 0)
|
|
{
|
|
FirstReaderEntered.Set();
|
|
AllowReader.Wait(TimeSpan.FromSeconds(5));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeProviderFactory : DbProviderFactory
|
|
{
|
|
private readonly FakeProviderState _state;
|
|
|
|
public FakeProviderFactory(FakeProviderState state)
|
|
{
|
|
_state = state;
|
|
}
|
|
|
|
public override DbConnection CreateConnection()
|
|
{
|
|
var connection = new FakeProviderConnection(_state);
|
|
lock (_state.Connections)
|
|
_state.Connections.Add(connection);
|
|
Interlocked.Increment(ref _state.CreatedConnections);
|
|
return connection;
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeProviderConnection : DbConnection
|
|
{
|
|
private readonly FakeProviderState _state;
|
|
private ConnectionState _connectionState = ConnectionState.Closed;
|
|
|
|
public FakeProviderConnection(FakeProviderState state)
|
|
{
|
|
_state = state;
|
|
}
|
|
|
|
public override string ConnectionString { get; set; }
|
|
public override string Database { get { return "fake"; } }
|
|
public override string DataSource { get { return "fake"; } }
|
|
public override string ServerVersion { get { return "1.0"; } }
|
|
public override ConnectionState State { get { return _connectionState; } }
|
|
|
|
public override void ChangeDatabase(string databaseName) { }
|
|
|
|
public override void Close()
|
|
{
|
|
if (_connectionState != ConnectionState.Closed)
|
|
{
|
|
_connectionState = ConnectionState.Closed;
|
|
Interlocked.Increment(ref _state.CloseCalls);
|
|
}
|
|
}
|
|
|
|
public override void Open()
|
|
{
|
|
if (_connectionState != ConnectionState.Open)
|
|
{
|
|
_connectionState = ConnectionState.Open;
|
|
Interlocked.Increment(ref _state.OpenCalls);
|
|
}
|
|
}
|
|
|
|
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
|
|
{
|
|
Interlocked.Increment(ref _state.BeginTransactionCalls);
|
|
return new FakeProviderTransaction(this, isolationLevel);
|
|
}
|
|
|
|
protected override DbCommand CreateDbCommand()
|
|
{
|
|
return new FakeProviderCommand(_state) { Connection = this };
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
Interlocked.Increment(ref _state.DisposeCalls);
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeProviderTransaction : DbTransaction
|
|
{
|
|
private readonly FakeProviderConnection _connection;
|
|
private readonly IsolationLevel _isolationLevel;
|
|
|
|
public FakeProviderTransaction(FakeProviderConnection connection, IsolationLevel isolationLevel)
|
|
{
|
|
_connection = connection;
|
|
_isolationLevel = isolationLevel;
|
|
}
|
|
|
|
public override IsolationLevel IsolationLevel { get { return _isolationLevel; } }
|
|
protected override DbConnection DbConnection { get { return _connection; } }
|
|
public override void Commit() { }
|
|
public override void Rollback() { }
|
|
}
|
|
|
|
internal sealed class FakeProviderCommand : DbCommand
|
|
{
|
|
private readonly FakeProviderState _state;
|
|
private readonly FakeProviderParameterCollection _parameters = new FakeProviderParameterCollection();
|
|
|
|
public FakeProviderCommand(FakeProviderState state)
|
|
{
|
|
_state = state;
|
|
}
|
|
|
|
public override string CommandText { get; set; }
|
|
public override int CommandTimeout { get; set; }
|
|
public override CommandType CommandType { get; set; }
|
|
public override bool DesignTimeVisible { get; set; }
|
|
public override UpdateRowSource UpdatedRowSource { get; set; }
|
|
protected override DbConnection DbConnection { get; set; }
|
|
protected override DbParameterCollection DbParameterCollection { get { return _parameters; } }
|
|
protected override DbTransaction DbTransaction { get; set; }
|
|
|
|
public override void Cancel() { }
|
|
public override int ExecuteNonQuery()
|
|
{
|
|
_state.RecordExecution(DbTransaction);
|
|
try
|
|
{
|
|
return 1;
|
|
}
|
|
finally
|
|
{
|
|
_state.ExitExecution();
|
|
}
|
|
}
|
|
|
|
public override object ExecuteScalar()
|
|
{
|
|
return ExecuteNonQuery();
|
|
}
|
|
|
|
public override void Prepare() { }
|
|
|
|
protected override DbParameter CreateDbParameter()
|
|
{
|
|
return new FakeProviderParameter();
|
|
}
|
|
|
|
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
|
|
{
|
|
_state.RecordReaderOpen(DbTransaction);
|
|
return new FakeProviderDataReader(_state);
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeProviderDataReader : DbDataReader
|
|
{
|
|
private readonly FakeProviderState _state;
|
|
private bool _closed;
|
|
|
|
public FakeProviderDataReader(FakeProviderState state)
|
|
{
|
|
_state = state;
|
|
}
|
|
|
|
public override object this[int ordinal] { get { return 1; } }
|
|
public override object this[string name] { get { return 1; } }
|
|
public override int Depth { get { return 0; } }
|
|
public override int FieldCount { get { return 1; } }
|
|
public override bool HasRows { get { return true; } }
|
|
public override bool IsClosed { get { return _closed; } }
|
|
public override int RecordsAffected { get { return 0; } }
|
|
|
|
public override bool GetBoolean(int ordinal) { return true; }
|
|
public override byte GetByte(int ordinal) { return 1; }
|
|
public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) { return 0; }
|
|
public override char GetChar(int ordinal) { return '1'; }
|
|
public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) { return 0; }
|
|
public override string GetDataTypeName(int ordinal) { return "Int32"; }
|
|
public override DateTime GetDateTime(int ordinal) { return DateTime.UtcNow; }
|
|
public override decimal GetDecimal(int ordinal) { return 1m; }
|
|
public override double GetDouble(int ordinal) { return 1d; }
|
|
public override System.Collections.IEnumerator GetEnumerator() { yield break; }
|
|
public override Type GetFieldType(int ordinal) { return typeof(int); }
|
|
public override float GetFloat(int ordinal) { return 1f; }
|
|
public override Guid GetGuid(int ordinal) { return Guid.Empty; }
|
|
public override short GetInt16(int ordinal) { return 1; }
|
|
public override int GetInt32(int ordinal) { return 1; }
|
|
public override long GetInt64(int ordinal) { return 1L; }
|
|
public override string GetName(int ordinal) { return "Value"; }
|
|
public override int GetOrdinal(string name) { return 0; }
|
|
public override string GetString(int ordinal) { return "1"; }
|
|
public override object GetValue(int ordinal) { return 1; }
|
|
public override int GetValues(object[] values) { values[0] = 1; return 1; }
|
|
public override bool IsDBNull(int ordinal) { return false; }
|
|
public override bool NextResult() { return false; }
|
|
public override bool Read() { return false; }
|
|
|
|
public override void Close()
|
|
{
|
|
if (!_closed)
|
|
{
|
|
_closed = true;
|
|
_state.ExitExecution();
|
|
}
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
Close();
|
|
base.Dispose(disposing);
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeProviderParameter : DbParameter
|
|
{
|
|
public override DbType DbType { get; set; }
|
|
public override ParameterDirection Direction { get; set; }
|
|
public override bool IsNullable { get; set; }
|
|
public override string ParameterName { get; set; }
|
|
public override string SourceColumn { get; set; }
|
|
public override object Value { get; set; }
|
|
public override bool SourceColumnNullMapping { get; set; }
|
|
public override int Size { get; set; }
|
|
public override void ResetDbType() { }
|
|
}
|
|
|
|
internal sealed class FakeProviderParameterCollection : DbParameterCollection
|
|
{
|
|
private readonly List<DbParameter> _items = new List<DbParameter>();
|
|
|
|
public override int Count { get { return _items.Count; } }
|
|
public override object SyncRoot { get { return this; } }
|
|
public override int Add(object value) { _items.Add((DbParameter)value); return _items.Count - 1; }
|
|
public override void AddRange(Array values) { foreach (object value in values) Add(value); }
|
|
public override void Clear() { _items.Clear(); }
|
|
public override bool Contains(object value) { return _items.Contains((DbParameter)value); }
|
|
public override bool Contains(string value) { return IndexOf(value) >= 0; }
|
|
public override void CopyTo(Array array, int index) { _items.ToArray().CopyTo(array, index); }
|
|
public override System.Collections.IEnumerator GetEnumerator() { return _items.GetEnumerator(); }
|
|
protected override DbParameter GetParameter(int index) { return _items[index]; }
|
|
protected override DbParameter GetParameter(string parameterName) { return _items[IndexOf(parameterName)]; }
|
|
public override int IndexOf(object value) { return _items.IndexOf((DbParameter)value); }
|
|
public override int IndexOf(string parameterName) { return _items.FindIndex(x => x.ParameterName == parameterName); }
|
|
public override void Insert(int index, object value) { _items.Insert(index, (DbParameter)value); }
|
|
public override void Remove(object value) { _items.Remove((DbParameter)value); }
|
|
public override void RemoveAt(int index) { _items.RemoveAt(index); }
|
|
public override void RemoveAt(string parameterName) { int i = IndexOf(parameterName); if (i >= 0) _items.RemoveAt(i); }
|
|
protected override void SetParameter(int index, DbParameter value) { _items[index] = value; }
|
|
protected override void SetParameter(string parameterName, DbParameter value) { _items[IndexOf(parameterName)] = value; }
|
|
}
|
|
}
|