【发布时间】: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