I\'m using a third party library which returns a data reader. I would like a simple way and as generic as possible to convert it into a List of objects.
For example, say
My version
Usage:
var Q = await Reader.GetTable<DbRoom>("SELECT id, name FROM rooms");
PgRoom is
public class DbRoom
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
Reader.GetTable contains:
using (var R = await Q.ExecuteReaderAsync())
{
List<T> Result = new List<T>();
Dictionary<int, PropertyInfo> Props = new Dictionary<int, PropertyInfo>();
foreach (var p in typeof(T).GetProperties())
{
for (int i = 0; i < R.FieldCount; i++)
{
if (p.GetCustomAttributes<ColumnAttribute>().FirstOrDefault(t => t.Name == R.GetName(i)) != null
&& p.PropertyType == R.GetFieldType(i))
{
Props.Add(i, p);
}
}
}
while (await R.ReadAsync())
{
T row = new T();
foreach (var kvp in Props)
{
kvp.Value.SetValue(row, R[kvp.Key]);
}
Result.Add(row);
}
return Result;
}
For .NET Core 2.0:
Here is an extension method that works with .NET CORE 2.0 to execute RAW SQL and map results to LIST of arbitrary types:
USAGE:
var theViewModel = new List();
string theQuery = @"SELECT * FROM dbo.Something";
theViewModel = DataSQLHelper.ExecSQL(theQuery,_context);
using Microsoft.EntityFrameworkCore;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
public static List ExecSQL(string query, myDBcontext context)
{
using (context)
{
using (var command = context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = query;
command.CommandType = CommandType.Text;
context.Database.OpenConnection();
using (var result = command.ExecuteReader())
{
List<T> list = new List<T>();
T obj = default(T);
while (result.Read())
{
obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in obj.GetType().GetProperties())
{
if (!object.Equals(result[prop.Name], DBNull.Value))
{
prop.SetValue(obj, result[prop.Name], null);
}
}
list.Add(obj);
}
return list;
}
}
}
}
I found this solution.
var cmd = ctx.Connection.CreateCommand();
T result = DbDataReaderdHelper.Fill<T>(cmd)
public static class DbDataReaderdHelper
{
public static List<T> Fill<T>(DbCommand dbCommand) where T : new()
{
List<T> result = new List<T>();
var reader = dbCommand.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Type type = typeof(T);
T obj = (T)Activator.CreateInstance(type);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
var value = reader[property.Name];
try
{
if (value != null)
{
var convertedValue = TypeDescriptor.GetConverter(property.PropertyType).ConvertFromInvariantString(value.ToString());
property.SetValue(obj, convertedValue);
}
}
catch {}
}
result.Add(obj);
}
}
reader.Close();
return result;
}
}
Like Magic
I personally HATE doing manual mapping in constructors, I'm also not a fan of doing my own reflection. So here's another solution courtesy of the wonderful (and fairly ubiquitous) Newtonsoft JSON lib.
It will only work if your property names exactly match the datareader column names, but it worked well for us.
...assumes you've got a datareader name "yourDataReader"...
var dt = new DataTable();
dt.Load(yourDataReader);
// creates a json array of objects
string json = Newtonsoft.Json.JsonConvert.SerializeObject(dt);
// this is what you're looking for right??
List<YourEntityType> list =
Newtonsoft.Json.JsonConvert
.DeserializeObject<List<YourEntityType>>(json);