Olivier's implementation is good. It uses generics and interfaces giving each object its own implementation of FillFromDataReader ().
You can do it further. Using convention, the entire data hydration code can be centralized and abstracted.
I'm going to assume that your class property names and column names are the same. If they are not, then the following code can be extended to add alias attributes to property names. Sometimes a property is calculated from other values โโof an object; this property cannot be hydrated. The Ignore attribute can be created and implemented in the underlying class.
public class DataAccess { /// <summary> /// Hydrates the collection of the type passes in. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="sql">The SQL.</param> /// <param name="connection">The connection.</param> /// <returns>List{``0}.</returns> public List<T> List<T>(string sql, string connection) where T: new() { List<T> items = new List<T>(); using (SqlCommand command = new SqlCommand(sql, new SqlConnection(connection))) { string[] columns = GetColumnsNames<T>(); var reader = command.ExecuteReader(CommandBehavior.CloseConnection); while (reader.Read()) { T item = new T(); foreach (var column in columns) { object val = reader.GetValue(reader.GetOrdinal(column)); SetValue(item, val, column); } items.Add(item); } command.Connection.Close(); } return items; } /// <summary> /// Sets the value. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="item">The item.</param> /// <param name="value">The value.</param> /// <param name="column">The column.</param> private void SetValue<T>(T item, object value, string column) { var property = item.GetType().GetProperty(column); property.SetValue(item, value, null); } /// <summary> /// Gets the columns names. /// </summary> /// <typeparam name="T"></typeparam> /// <returns>System.String[][].</returns> private string[] GetColumnsNames<T>() where T : new() { T item = new T(); return (from i in item.GetType().GetProperties() select i.Name).ToArray(); } }
There are a couple of caveats in the above code. The types DBNulls and Nullable are special cases and require special code from them. Usually I convert DBNull to null. I have never encountered a situation where I needed to distinguish between different ones. For Nullalbe types, simply define the Nullable type and process the code accordingly.
ORM will remove most of the headaches when working with data access. The downside is that you are connected to the DTO and the database schema. Of course, this problem may lie in the use of abstractions. Many companies still use strictly stored procedures; most ORMs fall when they have to consume stored procedures. They are simply not designed to work with stored procedures.
I wrote a data access structure called " Hypersonic ." This is on GitHub, it is specifically designed to work with stored procedures. The above code is an easy implementation of it.
source share