我正在开发一个以Oracle数据库为中心的项目(虽然老实说,我并不认为这很重要),我发现自己有相当多的重复代码,特别是异常。到目前为止,我看到的最好的方法是来自这个问题https://stackoverflow.com/a/1554/865868,它建议使用代表。在我尝试在我的项目中实现它之前,这看起来是完美的解决方案。我发现我遇到了一个不实用的案例。
让我稍微描述一下我的节目。我有两段处理数据库操作的代码。我的想法是调用一个返回DataTable的函数,称为LoadDataTable()。然后我有一个函数将列表中的项目插入到表中。
private void AddToList(List<string> itemList) {
try {
using (OracleConnection connection = new OracleConnection(connectionString)) {
connection.Open();
foreach (string item in itemList) {
using (OracleCommand command = new OracleCommand()) {
command.Connection = connection;
//Insert operation here
//......
command.ExecuteNonQuery();
}
}
} catch (OracleException ex) {
string messageboxtitle = "Database Exception";
switch (ex.Number) {
case 00001:
MessageBox.Show("Constraint Violation Error", messageboxtitle, MessageBoxButtons.OK);
break;
case 12154:
MessageBox.Show(string.Format("Connection Error: {0}", ex.Message), messageboxtitle);
break;
default:
MessageBox.Show(ex.ToString());
break;
}
}
}
private DataTable LoadDataTable() {
DataTable dataTable = new DataTable();
try {
using (OracleConnection connection = new OracleConnection(connectionString)) {
connection.Open();
using (OracleCommand command = new OracleCommand()) {
command.Connection = connection;
command.CommandText = sql;
command.CommandType = CommandType.Text;
using (OracleDataAdapter oda = new OracleDataAdapter(command)) {
oda.Fill(dataTable);
}
}
}
} catch (OracleException ex) {
string messageboxtitle = "Database Exception";
switch (ex.Number) {
case 12154:
MessageBox.Show(string.Format("Connection Error: {0}", ex.Message), messageboxtitle); //Duplicate Exception
break;
default:
MessageBox.Show(ex.ToString());
break;
}
}
return dataTable;
}
请记住,我必须重写并简化代码,以便我可以讨论它。无论如何,看着代表的例子,我很快意识到参数是一个问题。你不能在List<string>
类型中使用params,但是代表们的用处是毫无疑问的,因为我可以在一个集中的部分中查找不重复的异常。
private delegate void DatabaseOperation(List<string> itemList);
private void PerformDatabaseOperation(DatabaseOperation operation, List<string> itemList){
try {
operation(itemList);
} catch (OracleException ex) {
string messageboxtitle = "Database Exception";
switch (ex.Number) {
case 00001:
MessageBox.Show("Constraint Violation Error", messageboxtitle, MessageBoxButtons.OK);
break;
case 12154:
MessageBox.Show(string.Format("Connection Error: {0}", ex.Message), messageboxtitle);
break;
default:
MessageBox.Show(ex.ToString());
break;
}
}
}
private void AddToList(List<string> itemList) {
using (OracleConnection connection = new OracleConnection(connectionString)) {
connection.Open();
foreach (string item in itemList) {
using (OracleCommand command = new OracleCommand()) {
command.Connection = connection;
//Insert operation here
//......
command.ExecuteNonQuery();
}
}
}
使用方法:
List<string> itemList = new List<string>();
//code to fill list
PerformDatabaseOperation(AddToList, itemList);
现在的问题是我无法使用此委托实现LoadDataTable(),因为它没有任何参数。由于List不兼容,因此在委托上使用params
不起作用。我正在寻求改进我的编码技术以提高可用性和可读性,但我发现自己正在旋转我的轮子阅读关于这个主题的各种线程,主要是因为它们不够深入到一个不能真正解决问题的简单例子我发现自己现在。为了确保得到答案,让我提出一个最后的问题。我怎样才能编写避免重复异常的代码?
更新
对于任何想要解决类似问题的人,请参阅下文。保持在我的领域还有很多可以改进的代码。此外,有兴趣参与此处讨论的var
关键字的讨论,go here。我希望这会有所帮助:
private delegate void DatabaseOperation();
private void PerformDatabaseOperation(DatabaseOperation operation) {
try {
operation();
} catch (OracleException ex) {
string messageboxtitle = "Database Exception";
switch (ex.Number) {
case 00001:
MessageBox.Show("Constraint Violation Error", messageboxtitle, MessageBoxButtons.OK);
break;
case 12154:
MessageBox.Show(string.Format("Connection Error: {0}", ex.Message), messageboxtitle);
break;
default:
MessageBox.Show(ex.ToString());
break;
}
}
}
private void AddToList(List<string> itemList) {
using (OracleConnection connection = new OracleConnection(connectionString)) {
connection.Open();
foreach (string item in itemList) {
using (OracleCommand command = new OracleCommand()) {
command.Connection = connection;
//Insert operation here
//......
command.ExecuteNonQuery();
}
}
}
private DataTable LoadDataTable() {
DataTable dataTable = new DataTable();
using (OracleConnection connection = new OracleConnection(connectionString)) {
connection.Open();
using (OracleCommand command = new OracleCommand()) {
command.Connection = connection;
command.CommandText = sql;
command.CommandType = CommandType.Text;
using (OracleDataAdapter oda = new OracleDataAdapter(command)) {
oda.Fill(dataTable);
}
}
}
return dataTable;
}
答案 0 :(得分:2)
我建议你使用具有Oracle支持的微型ORM,例如PetaPoco。如果可以自动生成,为什么要编写所有代码?如果您想编写自己的数据访问层,我建议您为类创建CRUD方法,例如
public bool Insert(Person p)
{...}
您还可以考虑使用通用方法来执行此操作,例如:
public bool Insert<T>(T item)
{...}
答案 1 :(得分:2)
请记住,委托从父方法的主体中捕获变量。这也称为closure。因此,代理通常没有必要有参数列表。
var itemList = new List<string>();
PerformDatabaseOperation(
() => {
...
itemList.Add(...);
...
}
);
<强>更新强>
你会这样称呼:
List<string> itemList = new List<string>();
PerformDatabaseOperation(() => AddToList(itemList));
诀窍是将lambda表达式传递给没有参数的PerformDatabaseOperation
(空大括号()
); PerformDatabaseOperation
还有否 itemList
参数。 lambda表达式的主体在调用itemList
之前使用PerformDatabaseOperation
声明并初始化。剩下的就是C#编译魔术。
答案 2 :(得分:-1)
一种经常使用的传递变量参数的模式是将它们传递给构造函数。优点是构造函数不是接口的一部分。但是,它要求您使用对象而不是委托。
public interface IDatabaseOperation
{
void Execute();
}
public class LoadListDatabaseOperation : IDatabaseOperation
{
private List<string> _itemList;
public LoadListDatabaseOperation(List<string> itemList)
{
_itemList = itemList;
}
public void Execute()
{
...
// Fill the list here
...
}
}
传递对象的成本更高,但也具有优势,例如能够创建具有不同专业化程度的操作层次结构。您可能具有通用基本操作并派生专门用于特定类型的操作。您还可以使用属性作为标量返回值等。