/* * 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 SeenTransactions = new List(); public readonly List Connections = new List(); 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 _items = new List(); 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; } } }