I'd add a function:
string GetNullableString(DataRow row, string columnName)
{
return Row[columnName] == DBNull.Value ? null : (string)Row[columnName];
}
for reason of readabilty and better maintenance.
Accessing the columns by number could be a little faster than by name.
But major point is: optimized SQL query with appropriate indices on the table.
I do not use DataAdapters and DataSets, I prefer a simple DataReader - but that might be just a personal preference.
The use of try-catch-throw is a WTF. If you throw from the catch block, throw a "better" exception with the original exception as InnerException.
Those objects for use with Database which implement IDisposable could be wrapped in
using
blocks - that will make sure they are disposed of properly also in case of an exception. And declare them inside your function, not as class members.