【发布时间】:2013-12-05 02:34:28
【问题描述】:
我有一个 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。我正在使用 SqlLite 数据库,因为它是我在这台机器上安装的。但我很确定使用的数据库与这个问题无关,使用其他任何东西的结果都是一样的。
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>
请注意,我在那里也有一个按钮,它的点击处理程序也会抛出。在正确显示“异常捕获”消息框的情况下,这可以按预期工作。
另外请注意,我正在使用禁用异常过滤的自定义 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 抛出的异常被静默吃掉。这真的不太好。如果某些异常会被默默地吃掉,那么这将是对任何健壮性尝试的嘲弄。
【问题讨论】:
标签: c# wpf datagrid exception-handling