1

I am working on a project centered around an Oracle database (though honestly I don't expect that to matter) and I find myself have a fair amount of duplicate code, specifically exceptions. The best way I've seen so far is from this question https://stackoverflow.com/a/1554/865868, which suggests the use of delegates. This looked like the perfect solution until I tried to implement it in my project. I found that I had one case where it was not practical.

Let me describe my program a bit. I have two sections of code that deal with database operations. The idea is that I call a function that returns a DataTable, called LoadDataTable(). I then have a function that deals inserting items from a list into a table.

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;
}

Keep in mind I had to rewrite and simplify that code so I could discuss it. Anyway, looking at the delegate example I quickly realized that paramaters are an issue. You can't use params for List<string> types, but there is no doubt in the usefulness of the delegates as I would be able to one one centralized section for exceptions that are not duplicated.

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();
        }
    }
}

How to use:

List<string> itemList = new List<string>();
//code to fill list
PerformDatabaseOperation(AddToList, itemList);

Problem is now that I am unable to implement LoadDataTable() with this delegate as it does not have any parameters. The use of params on the delegate does not work since List is incompatible. I'm looking to improve my coding techniques for re usability and readability but I find myself spinning my wheels reading various threads on the subject, mostly since they do not go in depth enough beyond a simple example that doesn't really catch the problem I find myself having now. To ensure this gets answered let me pose a final question. How could I write code which would avoid duplicating exceptions?

UPDATE

For anyone looking to solve a similar problem, see below. Keep in my there is plenty more that can be done to improve the code. Also, anyone interested in the discussion around the var keyword discussed here, go here. I hope this helps:

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;
}
Community
  • 1
  • 1
Jeff
  • 1,727
  • 2
  • 17
  • 29
  • OMG dude your code is so much java like that it makes my head hurt. Why in the world are you doing `egyptian braces`?? and please use the `var` keyword. `var itemList = new List();` – Federico Berasategui May 13 '13 at 19:56
  • And you don't need a custom delegate for a simple method like that. Use `System.Action>` instead. – Federico Berasategui May 13 '13 at 19:58
  • 1
    And Why is your [DATA ACCESS LAYER messing with MessageBoxes!?!?](http://thedailywtf.com/Articles/Dear-Sybase-MessageBoxes-Don%E2%80%99t-Belong-In-Drivers.aspx) – Federico Berasategui May 13 '13 at 20:01
  • I very much hate wasting a line of code for the braces. So much screen real estate lost, but to each their own. As for the var keyword, why would I do that, it seems so vague? – Jeff May 13 '13 at 20:08
  • 1
    real estate lost?? what are you talking about your code is horrible. C# does not do egyptian braces. That belongs to crappy languages such as java. and the `var` keyword is for READABILITY. Something you really don't consider I guess. – Federico Berasategui May 13 '13 at 20:15
  • No need to be rude... I am here to learn a better way. I don't want to talk about my brace style, it's personal preference. How is the `var` keyword for readability? It's seems generic, where as I know exactly what `List` is. – Jeff May 13 '13 at 20:24
  • 1
    I have some `ObservableCollection` in one of my ViewModels. Would you rather go `ObservableCollection ViewModels = new ObservableCollection();` or use the `var` keyword? – Federico Berasategui May 13 '13 at 20:25
  • @HighCore while I personally agree with you on both counts, I guess those kind of things are better left to the developer (unless there are conventions defined, in which case Do It That Way OR ELSE). Also: Yes, don't do messageboxes in the Data Access Layer. There is absolutely no excuse for that, not even for debugging. – Yandros May 13 '13 at 20:25
  • 1
    @Rakshasas Since the var keyword is always in the same line as an assignment, the vagueness is not really an issue; especially when you're using the 'new' keyword. It's not as big an issue as HighCore seems to believe, though. – Yandros May 13 '13 at 20:29
  • I think I see your point. I guess it is more readable considering you know what it is based off of both intellisense and what is contained on the right hand side. You all are right of course in regards to the MessageBoxes, I will read into best practices for how to better handle that as asking that question here takes away from the original question. Thank you. – Jeff May 13 '13 at 20:33

3 Answers3

2

I would suggest that you use a micro ORM such as PetaPoco which has Oracle support. Why write all that code if it can be auto generated? If you want to write your own data access layer I suggest you create CRUD methods for your classes such as

 public bool Insert(Person p)
 {...} 

You can also look into using generic methods to do this such as:

public bool Insert<T>(T item)
{...} 
Atomic Star
  • 5,427
  • 4
  • 39
  • 48
2

Keep in mind that delegates capture variables from the parent method's body. This is also called a closure. Therefore it is often not necessary for the delegate to have a parameter list.

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

UPDATE

You would call it like this:

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

The trick is to pass a lambda expression to the PerformDatabaseOperation that has no parameters (the empty braces ()); also PerformDatabaseOperation has no itemList parameter. The body of the lambda expression uses itemList declared and initialized just before the call to PerformDatabaseOperation. C# compiler magic does the rest.

Community
  • 1
  • 1
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • I find this intriguing but the pieces aren't quite coming together in my head. I am very new to the concept of delegate so I am wondering quite a few things. In my code I removed the parameters `private delegate void DatabaseOperation();`. I am left wondering how exactly AddToList is called from your use of `PerformDatabaseOperation`. I presume AddToList would still be coded to accept a parameter? – Jeff May 13 '13 at 21:28
  • I added an example for calling `PerformDatabaseOperation` with `AddToList`. – Olivier Jacot-Descombes May 14 '13 at 13:18
  • Thank you, my understanding was slightly off and I was able to solve my issue. – Jeff May 14 '13 at 14:18
-1

One very often used pattern of passing varying parameters is to pass them to a constructor. The advantage is that constructors are not part of interfaces. However, it requires you to work with objects instead of delegates.

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

Passing objects is a bit more costly but has also advantages, like being able to create hierarchies of operations having different degrees of specialization. You might have generic base operations and derive operations specialized for specific types. You can also use properties for scalar returns values etc.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • -1 this encourages the java-like hack of using interfaces to mimic C#'s delegates. Bad idea. – Federico Berasategui May 13 '13 at 20:03
  • @HighCore: This is also called **Command Pattern**, a very powerful pattern. In this case it could possibly also be considered as strategy pattern, as different database access strategies are injected. – Olivier Jacot-Descombes May 13 '13 at 20:06
  • Not really. Having more code to achieve the same results is always and indication of a poor design. Delegates is the way to go here. – Federico Berasategui May 13 '13 at 20:07
  • More code, okay, but it has also advantages, as you can create hierarchies of commands; e.g., you might have a base list load command and derive specialized load list commands from it that perform some filtering (using Regex patterns for instance). The possibilities are endless. – Olivier Jacot-Descombes May 13 '13 at 20:20
  • That can also be achieved with delegates. In a cleaner way. My point still stands. – Federico Berasategui May 13 '13 at 20:22