C# 与数据库代码简化

Posted

技术标签:

【中文标题】C# 与数据库代码简化【英文标题】:C# with Database code simplication 【发布时间】:2013-05-07 22:55:57 【问题描述】:

我正在从事一个以 Oracle 数据库为中心的项目(尽管老实说,我并不认为这很重要),我发现自己有大量重复的代码,特别是例外。到目前为止我看到的最好的方法是来自这个问题https://***.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&lt;string&gt; 类型,但毫无疑问,代表的用处是因为我可以将一个集中的部分用于不重复的异常。

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;

【问题讨论】:

天啊老兄,你的代码太像 java 了,让我头疼。为什么你在做egyptian braces??请使用var 关键字。 var itemList = new List&lt;string&gt;(); 为什么你的DATA ACCESS LAYER messing with MessageBoxes!?!? 房产丢失??你在说什么你的代码太可怕了。 C# 不做埃及大括号。那属于诸如java之类的蹩脚语言。 var 关键字用于可读性。我猜你真的不认为的东西。 我的一个 ViewModel 中有一些 ObservableCollection&lt;AdmissionSummaryDataRowViewModel&gt;。你宁愿去ObservableCollection&lt;AdmissionSummaryDataRowViewModel&gt; ViewModels = new ObservableCollection&lt;AdmissionSummaryDataRowViewModel&gt;();还是使用var关键字? @Rakshasas 因为 var 关键字总是与赋值在同一行,所以模糊性并不是真正的问题;特别是当您使用“新”关键字时。不过,这并不像 HighCore 认为的那么大。 【参考方案1】:

请记住,委托从父方法的主体中捕获变量。这也称为closure。因此,委托通常不需要有参数列表。

var itemList = new List<string>();
PerformDatabaseOperation(
    () => 
        ...
        itemList.Add(...);
        ...
    
);

更新

你可以这样称呼它:

List<string> itemList = new List<string>();
PerformDatabaseOperation(() => AddToList(itemList));

诀窍是将 lambda 表达式传递给没有参数的PerformDatabaseOperation(空括号()); PerformDatabaseOperation 也有 no itemList 参数。 lambda 表达式的主体使用在调用PerformDatabaseOperation 之前声明和初始化的itemList。剩下的就是 C# 编译器。

【讨论】:

我觉得这很有趣,但这些碎片在我脑海中并没有完全融合在一起。我对代表的概念很陌生,所以我想知道很多事情。在我的代码中,我删除了参数private delegate void DatabaseOperation();。我想知道您使用PerformDatabaseOperation 究竟是如何调用 AddToList 的。我认为 AddToList 仍将被编码为接受参数? 我添加了一个使用AddToList 调用PerformDatabaseOperation 的示例。 谢谢,我的理解有点偏离,我能够解决我的问题。【参考方案2】:

我建议您使用具有 Oracle 支持的微 ORM,例如 PetaPoco。如果可以自动生成,为什么还要编写所有这些代码?如果您想编写自己的数据访问层,我建议您为您的类创建 CRUD 方法,例如

 public bool Insert(Person p)
 ... 

您还可以考虑使用通用方法来执行此操作,例如:

public bool Insert<T>(T item)
... 

【讨论】:

【参考方案3】:

传递不同参数的一种非常常用的模式是将它们传递给构造函数。优点是构造函数不是接口的一部分。但是,它要求您使用对象而不是委托。

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
        ...
    

传递对象的成本更高,但也有一些优势,比如能够创建具有不同专业化程度的操作层次结构。您可能有通用的基本操作并派生专门针对特定类型的操作。您还可以将属性用于标量返回值等。

【讨论】:

-1 这鼓励了类似 java 的使用接口来模仿 C# 的委托的 hack。坏主意。 @HighCore:这也叫命令模式,非常强大的模式。在这种情况下,它也可能被视为策略模式,因为注入了不同的数据库访问策略。 并非如此。拥有更多代码来实现相同的结果总是表明设计不佳。代表是去这里的方式。 更多代码,好吧,但它也有优点,因为您可以创建命令的层次结构;例如,您可能有一个基本列表加载命令并从中派生专门的加载列表命令来执行一些过滤(例如使用正则表达式模式)。可能性是无穷无尽的。 这也可以通过委托来实现。以更清洁的方式。我的观点仍然成立。

以上是关于C# 与数据库代码简化的主要内容,如果未能解决你的问题,请参考以下文章

C#核编之内建数据类型

C# 7.0 语言新特性

C#新特性

C# 尝试操作Json数据进行接口开发(代码)

C# 尝试操作Json数据进行接口开发(代码)

C# 尝试操作Json数据进行接口开发(代码)