【问题标题】:Oracle Notification DapperOracle 通知小巧玲珑
【发布时间】:2019-03-03 13:15:27
【问题描述】:

我正在尝试使用 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 实现这一目标?

编辑 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 类中捕获对 dbs 的更改

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

}

我做错了什么,因为我真的看不出问题出在哪里?

【问题讨论】:

    标签: c# wpf oracle dapper caliburn.micro


    【解决方案1】:

    员工列表需要是一个可观察的列表,即告诉潜在的侦听器,即视图模型绑定,当它被修改时。一个简单的列表不会这样做。将Employees 列表更改为ObservableCollection&lt;Employee&gt;。在适当的时候,您几乎肯定需要在您的 Employee 类中实现 IChangeNotification 以及更改属性时。

    【讨论】:

    • 遗憾的是,我已经这样做了,但没有成功,我为 Employee 模型类中的每个属性实现了 INotifyPropertyChanged,更改为 ObservableCollection 但即使我添加/删除项目也不会更新视图在收藏上,或者如果我要换一个。我应该在 ViewModel 中做什么?
    猜你喜欢
    • 2012-04-08
    • 1970-01-01
    • 2011-10-15
    • 1970-01-01
    • 2012-04-08
    • 1970-01-01
    • 2022-08-18
    • 1970-01-01
    • 2019-11-10
    相关资源
    最近更新 更多