Oracle Notification Dapper - PullRequest
       13

Oracle Notification Dapper

0 голосов
/ 03 марта 2019

Я пытаюсь использовать уведомление о непрерывном запросе Oracle, но в WPF MVVM с Dapper и Caliburn.Micro.

Я установил статический класс OracleConnector, где dapper запрашивает db и заполняет список.как это:

List<Employees> List = cnn.Query<Employees>(OracleDynamicParameters.sql, param: dyParam).AsList();

В моем представлении я привязал DataGrid к этому списку: <DataGrid VerticalScrollBarVisibility="Auto" ItemsSource="{Binding Employees}"/>

У меня есть событие ниже, которое запускается при изменении строки вбаза данных:

private void OnMyNotificaton(object sender, OracleNotificationEventArgs args)
    {
        MessageBox.Show("Result set has changed.", "Notification Alert",
         MessageBoxButton.OK, MessageBoxImage.Exclamation);

        // Append ROWID(s) to the base query to retrieve just modified row(s)
        DataRow detailRow = args.Details.Rows[0];
        string rowid = detailRow["Rowid"].ToString();
        string sqlUpdate = sqlSelect + "where rowid = \'" + rowid + "\'";

        // Append on to the sqlUpdate statement if there are additional 
        // updated rows
        for (int i = 1; i < args.Details.Rows.Count; i++)
        {
            detailRow = args.Details.Rows[i];
            rowid = detailRow["Rowid"].ToString();
            sqlUpdate = sqlUpdate + " or rowid = \'" + rowid + "\'";
        }

        // Refresh changed data


        using (OracleConnection con2 = new OracleConnection(constr))
        {
            OracleConnector.List.Clear();
            OracleConnector.List = con2.Query<Employees>(sqlUpdate, new DynamicParameters()).AsList();

        }

Как я могу обновить эту строку в моем списке, а также уведомить VieModel об изменениях списка?

Как вы видите, я просто очищаю свой список и пытаюсь сделать запросснова базу данных с dapper и добавить результат в список.Проблема только в том, что строки снова вставляются в список, а моя DataGrid не обновляется.

В примере Oracle используется OracleDataAdapter, который заполняет DataSet и связывает его с DataGrid следующим образом:

        // Refresh changed data

        OracleConnection con2 = new OracleConnection(constr); 
        OracleCommand cmd2 = new OracleCommand(sqlUpdate, con2);
        con2.Open();
        OracleDataAdapter da2 = new OracleDataAdapter(cmd2);
        da2.Fill(ds, tablename);

Как мне этого добиться с помощью Dapper?

EDIT 1

Я прочитал несколько советов о том, что ObservableCollection не уведомляет об изменениях элементов внутри и кого-либо еще.предложил расширить его следующим образом:

public class FullyObservableCollection<T> : ObservableCollection<T>, INotifyPropertyChanged
     where T : INotifyPropertyChanged
{
    /// <summary>
    /// Occurs when a property is changed within an item.
    /// </summary>
    public event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;

    public FullyObservableCollection() : base()
    { }

    public FullyObservableCollection(List<T> list) : base(list)
    {
        ObserveAll();
    }

    public FullyObservableCollection(IEnumerable<T> enumerable) : base(enumerable)
    {
        ObserveAll();
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Remove ||
            e.Action == NotifyCollectionChangedAction.Replace)
        {
            foreach (T item in e.OldItems)
                item.PropertyChanged -= ChildPropertyChanged;
        }

        if (e.Action == NotifyCollectionChangedAction.Add ||
            e.Action == NotifyCollectionChangedAction.Replace)
        {
            foreach (T item in e.NewItems)
                item.PropertyChanged += ChildPropertyChanged;
        }

        base.OnCollectionChanged(e);
    }

    protected void OnItemPropertyChanged(ItemPropertyChangedEventArgs e)
    {
        ItemPropertyChanged?.Invoke(this, e);
    }

    protected void OnItemPropertyChanged(int index, PropertyChangedEventArgs e)
    {
        OnItemPropertyChanged(new ItemPropertyChangedEventArgs(index, e));
    }

    protected override void ClearItems()
    {
        foreach (T item in Items)
            item.PropertyChanged -= ChildPropertyChanged;

        base.ClearItems();
    }

    private void ObserveAll()
    {
        foreach (T item in Items)
            item.PropertyChanged += ChildPropertyChanged;
    }

    private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        T typedSender = (T)sender;
        int i = Items.IndexOf(typedSender);

        if (i < 0)
            throw new ArgumentException("Received property notification from item not in collection");

        OnItemPropertyChanged(i, e);
    }
}

/// <summary>
/// Provides data for the <see cref="FullyObservableCollection{T}.ItemPropertyChanged"/> event.
/// </summary>
public class ItemPropertyChangedEventArgs : PropertyChangedEventArgs
{
    /// <summary>
    /// Gets the index in the collection for which the property change has occurred.
    /// </summary>
    /// <value>
    /// Index in parent collection.
    /// </value>
    public int CollectionIndex { get; }

    /// <summary>
    /// Initializes a new instance of the <see cref="ItemPropertyChangedEventArgs"/> class.
    /// </summary>
    /// <param name="index">The index in the collection of changed item.</param>
    /// <param name="name">The name of the property that changed.</param>
    public ItemPropertyChangedEventArgs(int index, string name) : base(name)
    {
        CollectionIndex = index;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="ItemPropertyChangedEventArgs"/> class.
    /// </summary>
    /// <param name="index">The index.</param>
    /// <param name="args">The <see cref="PropertyChangedEventArgs"/> instance containing the event data.</param>
    public ItemPropertyChangedEventArgs(int index, PropertyChangedEventArgs args) : this(index, args.PropertyName)
    { }
}

При этом кажется, что событие запускается, когда элементы добавляются / удаляются / изменяются, но, опять же, ViewModel не знает об этих изменениях, а View не обновляется..

Вот мой класс модели:

public class Employees : ObservableObject
{
    private double _salary;
    private int _employeeId;
    private string firstName;
    private string lastName;
    private string email;
    private string phoneNumber;


    public int EMPLOYEE_ID {
        get
        {
            return _employeeId;
        }
        set
        {
            _employeeId = value;
            OnPropertyChanged("EMPLOYEE_ID");

        }
    }
    public string FIRST_NAME {
        get
        {
            return firstName;
        }
        set
        {
            firstName = value;
            OnPropertyChanged("FIRST_NAME");
        }
    }
    public string LAST_NAME
    {
        get
        {
            return lastName;
        }
        set
        {
            lastName = value;
            OnPropertyChanged("LAST_NAME");
        }
    }
    public string EMAIL
    {
        get
        {
            return email;
        }
        set
        {
            email = value;
            OnPropertyChanged("EMAIL");
        }
    }
    public string PHONE_NUMBER
    {
        get
        {
            return phoneNumber;
        }
        set
        {
            phoneNumber = value;
            OnPropertyChanged("PHONE_NUMBER");
        }
    }
    public double SALARY {
        get
        {
            return _salary;
        }

        set
        {
            _salary = value;
            OnPropertyChanged("SALARY");
        }
    }

}

и базовый класс модели

public class ObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        if (propertyName == null)
        {
            throw new ArgumentNullException(nameof(propertyName));
        }

        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

}

Список заполняется в OracleConnector.cs

public class OracleConnector
{
    public static FullyObservableCollection<Employees> List;

    private static string LoadConnectionString(string id = "HR")
    {
        return ConfigurationManager.ConnectionStrings[id].ConnectionString.ToString();
    }

    public static FullyObservableCollection<Employees> GetEmployeeRepositorys(string connectionString)
    {

         using (IDbConnection cnn = new OracleConnection(LoadConnectionString(connectionString)))
        {
            var dyParam = new OracleDynamicParameters();

            try
            {

                var output = cnn.Query<Employees>(OracleDynamicParameters.sqlSelect, param: dyParam);
                List = new FullyObservableCollection<Employees>(output);


            }
            catch (OracleException ex)
            {
                MessageBox.Show("Connection to database is not available.\n" + ex.Message, "Database not available", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            return List;

        }
    }

}

и изменения в БД отслеживаются в классе OracleDynamicParameters

public class OracleDynamicParameters : SqlMapper.IDynamicParameters
{
    private readonly DynamicParameters dynamicParameters = new DynamicParameters();
    private readonly List<OracleParameter> oracleParameters = new List<OracleParameter>();


    public static string tablename = "Employees";
    public static string constr = "User Id=hr;Password=hr;Pooling=false;Data Source=ORCL;";
    public static string sqlSelect = "select employee_id, first_name, " +
                                     "last_name, salary from employees ";
    public static string sql = sqlSelect + "where employee_id < 200";
    public static DataSet ds = new DataSet();


    public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction, object value = null, int? size = null)
    {
        OracleParameter oracleParameter;
        if (size.HasValue)
        {
            oracleParameter = new OracleParameter(name, oracleDbType, size.Value, value, direction);
        }
        else
        {
            oracleParameter = new OracleParameter(name, oracleDbType, value, direction);
        }

        oracleParameters.Add(oracleParameter);
    }

    public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction)
    {
        var oracleParameter = new OracleParameter(name, oracleDbType, direction);
        oracleParameters.Add(oracleParameter);
    }

    public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
    {
        ((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);

        if (command is OracleCommand oracleCommand)
        {
            oracleCommand.AddRowid = true;

            OracleDependency dep = new OracleDependency(oracleCommand);

            oracleCommand.Notification.IsNotifiedOnce = false;
            dep.OnChange += Dep_OnChange;
            OracleDataAdapter da = new OracleDataAdapter(oracleCommand)
            {
                MissingSchemaAction = MissingSchemaAction.AddWithKey
            };

            oracleCommand.Parameters.AddRange(oracleParameters.ToArray());
        }
    }

    private void Dep_OnChange(object sender, OracleNotificationEventArgs args)
    {
        MessageBox.Show("Result set has changed.", "Notification Alert", MessageBoxButton.OK, MessageBoxImage.Exclamation);

        // Append ROWID(s) to the base query to retrieve just modified row(s)
        DataRow detailRow = args.Details.Rows[0];
        string rowid = detailRow["Rowid"].ToString();
        string sqlUpdate = sqlSelect + "where rowid = \'" + rowid + "\'";

        // Append on to the sqlUpdate statement if there are additional 
        // updated rows
        for (int i = 1; i < args.Details.Rows.Count; i++)
        {
            detailRow = args.Details.Rows[i];
            rowid = detailRow["Rowid"].ToString();
            sqlUpdate = sqlUpdate + " or rowid = \'" + rowid + "\'";
        }

        // Refresh changed data

        OracleConnection con2 = new OracleConnection(constr);
        OracleCommand cmd2 = new OracleCommand(sqlUpdate, con2);
        con2.Open();
        OracleDataAdapter da2 = new OracleDataAdapter(cmd2);
        da2.Fill(ds, tablename);
        OracleConnector.List.Clear();

        OracleConnector.List = new FullyObservableCollection<Employees>(ds.Tables["Employees"].AsEnumerable().Select(p => new Employees
        {
            EMPLOYEE_ID = p.Field<int>("EMPLOYEE_ID"),
            FIRST_NAME = p.Field<string>("FIRST_NAME"),
            LAST_NAME = p.Field<string>("LAST_NAME")
        }));
    }
}

и, наконец, ShellViewModel

public class ShellViewModel : Conductor<object>
{
    private FullyObservableCollection<Employees> _employees;
    private Employees _selectedEmployee;

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public Employees SelectedEmployee
    {
        get { return _selectedEmployee; }
        set
        {
            _selectedEmployee = value;
            NotifyOfPropertyChange(() => SelectedEmployee);
        }
    }

    public FullyObservableCollection<Employees> Employees
    {
        get { return _employees; }
        set
        {
            _employees = value;
            NotifyOfPropertyChange(() => Employees);


        }
    }

    #region Constructor

    public ShellViewModel()
    {
        _employees = new FullyObservableCollection<Employees>(OracleConnector.GetEmployeeRepositorys("HR"));

    }

    #endregion

}

Что я делаю не так, потому что я действительно не вижугде проблема?

1 Ответ

0 голосов
/ 04 марта 2019

Список сотрудников должен быть наблюдаемым списком, т. Е. Сообщающим потенциальному слушателю, т. Е. Привязку модели представления, когда он был изменен.Простой список не делает этого.Измените список Employees на ObservableCollection<Employee>.Со временем вам почти наверняка понадобится реализовать IChangeNotification в вашем классе Employee также при изменении свойств.

...