using System; using System.Data; using System.Data.Common; using System.Threading; using System.Threading.Tasks; namespace Npgsql; /// /// Represents the method that handles the events. /// /// The source of the event. /// An that contains the event data. public delegate void NpgsqlRowUpdatedEventHandler(object sender, NpgsqlRowUpdatedEventArgs e); /// /// Represents the method that handles the events. /// /// The source of the event. /// An that contains the event data. public delegate void NpgsqlRowUpdatingEventHandler(object sender, NpgsqlRowUpdatingEventArgs e); /// /// This class represents an adapter from many commands: select, update, insert and delete to fill a . /// [System.ComponentModel.DesignerCategory("")] public sealed class NpgsqlDataAdapter : DbDataAdapter { /// /// Row updated event. /// public event NpgsqlRowUpdatedEventHandler? RowUpdated; /// /// Row updating event. /// public event NpgsqlRowUpdatingEventHandler? RowUpdating; /// /// Default constructor. /// public NpgsqlDataAdapter() {} /// /// Constructor. /// /// public NpgsqlDataAdapter(NpgsqlCommand selectCommand) => SelectCommand = selectCommand; /// /// Constructor. /// /// /// public NpgsqlDataAdapter(string selectCommandText, NpgsqlConnection selectConnection) : this(new NpgsqlCommand(selectCommandText, selectConnection)) {} /// /// Constructor. /// /// /// public NpgsqlDataAdapter(string selectCommandText, string selectConnectionString) : this(selectCommandText, new NpgsqlConnection(selectConnectionString)) {} /// /// Create row updated event. /// protected override RowUpdatedEventArgs CreateRowUpdatedEvent(DataRow dataRow, IDbCommand? command, System.Data.StatementType statementType, DataTableMapping tableMapping) => new NpgsqlRowUpdatedEventArgs(dataRow, command, statementType, tableMapping); /// /// Create row updating event. /// protected override RowUpdatingEventArgs CreateRowUpdatingEvent(DataRow dataRow, IDbCommand? command, System.Data.StatementType statementType, DataTableMapping tableMapping) => new NpgsqlRowUpdatingEventArgs(dataRow, command, statementType, tableMapping); /// /// Raise the RowUpdated event. /// /// protected override void OnRowUpdated(RowUpdatedEventArgs value) { //base.OnRowUpdated(value); if (value is NpgsqlRowUpdatedEventArgs args) RowUpdated?.Invoke(this, args); //if (RowUpdated != null && value is NpgsqlRowUpdatedEventArgs args) // RowUpdated(this, args); } /// /// Raise the RowUpdating event. /// /// protected override void OnRowUpdating(RowUpdatingEventArgs value) { if (value is NpgsqlRowUpdatingEventArgs args) RowUpdating?.Invoke(this, args); } /// /// Delete command. /// public new NpgsqlCommand? DeleteCommand { get => (NpgsqlCommand?)base.DeleteCommand; set => base.DeleteCommand = value; } /// /// Select command. /// public new NpgsqlCommand? SelectCommand { get => (NpgsqlCommand?)base.SelectCommand; set => base.SelectCommand = value; } /// /// Update command. /// public new NpgsqlCommand? UpdateCommand { get => (NpgsqlCommand?)base.UpdateCommand; set => base.UpdateCommand = value; } /// /// Insert command. /// public new NpgsqlCommand? InsertCommand { get => (NpgsqlCommand?)base.InsertCommand; set => base.InsertCommand = value; } // Temporary implementation, waiting for official support in System.Data via https://github.com/dotnet/runtime/issues/22109 internal async Task Fill(DataTable dataTable, bool async, CancellationToken cancellationToken = default) { var command = SelectCommand; var activeConnection = command?.Connection ?? throw new InvalidOperationException("Connection required"); var originalState = ConnectionState.Closed; try { originalState = activeConnection.State; if (ConnectionState.Closed == originalState) await activeConnection.Open(async, cancellationToken); var dataReader = await command.ExecuteReader(CommandBehavior.Default, async, cancellationToken); try { return await Fill(dataTable, dataReader, async, cancellationToken); } finally { if (async) await dataReader.DisposeAsync(); else dataReader.Dispose(); } } finally { if (ConnectionState.Closed == originalState) activeConnection.Close(); } } async Task Fill(DataTable dataTable, NpgsqlDataReader dataReader, bool async, CancellationToken cancellationToken = default) { dataTable.BeginLoadData(); try { var rowsAdded = 0; var count = dataReader.FieldCount; var columnCollection = dataTable.Columns; for (var i = 0; i < count; ++i) { var fieldName = dataReader.GetName(i); if (!columnCollection.Contains(fieldName)) { var fieldType = dataReader.GetFieldType(i); var dataColumn = new DataColumn(fieldName, fieldType); columnCollection.Add(dataColumn); } } var values = new object[count]; while (async ? await dataReader.ReadAsync(cancellationToken) : dataReader.Read()) { dataReader.GetValues(values); dataTable.LoadDataRow(values, true); rowsAdded++; } return rowsAdded; } finally { dataTable.EndLoadData(); } } } #pragma warning disable 1591 public class NpgsqlRowUpdatingEventArgs : RowUpdatingEventArgs { public NpgsqlRowUpdatingEventArgs(DataRow dataRow, IDbCommand? command, System.Data.StatementType statementType, DataTableMapping tableMapping) : base(dataRow, command, statementType, tableMapping) {} } public class NpgsqlRowUpdatedEventArgs : RowUpdatedEventArgs { public NpgsqlRowUpdatedEventArgs(DataRow dataRow, IDbCommand? command, System.Data.StatementType statementType, DataTableMapping tableMapping) : base(dataRow, command, statementType, tableMapping) {} } #pragma warning restore 1591