我有一个WPF应用程序,它打开一个数据库表,用数据库中的表的内容填充DataTable,然后使用System.Windows.Controls.DataGrid提供它的视图。更新数据库是为了响应DataGrid中的用户输入。
用于此演示的数据库中的表没有主键,因此在插入数据库时工作正常,尝试更新现有值将引发异常。这是预期的,我知道如何解决这个问题,这不是问题所在。问题是,在DataAdapter上调用Update时抛出的异常会被静默吃掉。我需要这个例外来将堆栈传播到可以合理处理的位置。这段代码只是一个演示,在我的实际代码中,生成DataTable的程序集(包含发生异常的处理程序)是一个可重用的低级程序集,没有UI依赖。
以下是App.xaml.cs中的异常捕获处理程序
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
DispatcherUnhandledException += OnCurrent_DispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += OnCurrentDomain_UnhandledException;
}
private void OnCurrent_DispatcherUnhandledException(object sender,
DispatcherUnhandledExceptionEventArgs args)
{
MessageBox.Show(args.Exception.Message, "Exception Caught");
args.Handled = true;
}
private void OnCurrentDomain_UnhandledException(object sender,
UnhandledExceptionEventArgs args)
{
MessageBox.Show(args.ExceptionObject.ToString(), "Exception Caught");
}
}
这是我的MainWindow.xaml.cs。我正在使用SqLite数据库,因为它是我在这台机器上安装的。但我很确定使用的数据库与此问题无关,使用其他任何结果都是相同的。
public partial class MainWindow : Window
{
public MainWindow()
{
InitLocalDatabase();
InitializeComponent();
}
public DataTable Table { get; private set; }
private void InitLocalDatabase()
{
string currentDirectory = Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location);
string databaseName = Path.Combine(currentDirectory, "testdb.sqlite");
string connectionString = "Data Source=" + databaseName + ";";
if (!File.Exists(databaseName))
{
// Create and open database.
SQLiteConnection.CreateFile(databaseName);
_connection = new SQLiteConnection(connectionString);
_connection.Open();
// Create table in database.
const string createTable = "create table Table1 (Column1 int, Column2 int)";
using (SQLiteCommand cmd = new SQLiteCommand(createTable, _connection))
{
cmd.ExecuteNonQuery();
}
// Add data to table.
const string addRow = "insert into Table1 values ({0}, {1})";
for (int i = 0; i < 5; i += 2)
{
using (SQLiteCommand cmd = new SQLiteCommand(
string.Format(addRow, i, i + 1), _connection))
{
cmd.ExecuteNonQuery();
}
}
}
else
{
_connection = new SQLiteConnection(connectionString);
}
// Create the DataAdapter and DataTable.
_dataAdapter = new SQLiteDataAdapter("select * from Table1", _connection);
SQLiteCommandBuilder cb = new SQLiteCommandBuilder(_dataAdapter);
Table = new DataTable();
_dataAdapter.Fill(Table);
Table.RowChanged += OnDataTable_RowChanged;
}
private void OnDataTable_RowChanged(object sender, DataRowChangeEventArgs args)
{
try
{
Debug.Assert(Dispatcher.CheckAccess()); // Verify UI thread.
_dataAdapter.Update(Table);
}
catch (Exception)
{
MessageBox.Show("Throwing exception");
throw new Exception("Shit happens"); // This is eaten.
}
}
private void OnButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Throwing exception");
throw new Exception("Shit happens!!"); // This is not eaten.
}
protected override void OnClosing(CancelEventArgs args)
{
base.OnClosing(args);
if(_connection != null)
{
_connection.Dispose();
_connection = null;
}
}
private SQLiteConnection _connection;
private SQLiteDataAdapter _dataAdapter;
}
我的MainWindow.xaml只是
<StackPanel>
<Button Click="OnButton_Click">Button 1</Button>
<DataGrid Height="200" ItemsSource="{local:ThrowBinding Table, ElementName=_this}"/>
</StackPanel>
注意我也有一个Button也点击处理程序也会抛出。这可以正常工作,正确显示“Exception Caught”MessageBox。
另请注意,我正在使用禁用异常过滤的自定义Binding子类。代码如下。
public class ThrowBinding : Binding
{
public ThrowBinding()
{
Init();
}
public ThrowBinding(string path)
: base(path)
{
Init();
}
private void Init()
{
UpdateSourceExceptionFilter = _exceptionFilter;
ValidationRules.Add(_validationRule);
}
private static object ExceptionFilter(object bindingExpression, Exception e)
{
throw e;
}
private static readonly UpdateSourceExceptionFilterCallback _exceptionFilter = ExceptionFilter;
private static readonly ExceptionValidationRule _validationRule = new ExceptionValidationRule();
}
我使用AnyCpu和x86配置构建,结果相同,OnDataTable_RowChanged在尝试编辑现有值时抛出的异常是静默使用的。这真的不太好。如果某些例外情况会被悄悄地吃掉,那么这将是对任何鲁棒性尝试的嘲弄。
答案 0 :(得分:1)
似乎有一个错误,你实际上无法捕获从RowChanged事件抛出的异常。 Here是他们的“按设计”解释。 你可以做的是使用RowChanging事件或尝试设置ContinueUpdateOnError,然后使用GetErrors方法检查你的行是否有更新时发生的错误。