I'm writing an SQL Data Mapper that would map queries to dynamic types (I've already experienced mapping sql to strongly typed objects through reflection)
This time I want to make a sql call then return an Ienumerable.
This is what I have so far:
public class DynamicMapper
{
private class ReaderColumnMap
{
public string Column { get; }
public Type Type { get; }
public ReaderColumnMap(string column, Type type)
{
Column = column;
Type = type;
}
}
private Func<IDataReader, dynamic> MappingMethod;
public DynamicMapper(IDataReader schemaSource)
{
MappingMethod = CreateMapper(schemaSource);
}
private Func<IDataReader, dynamic> CreateMapper(IDataReader reader)
{
IDictionary<int, ReaderColumnMap> propertyMappingsCache = new Dictionary<int, ReaderColumnMap>();
//The schema of sql table involves the column name and data type for each columns.
DataTable schema = reader.GetSchemaTable();
DataColumnCollection dataColumns = schema.Columns;
//Gets the column name ordinal and data type ordinal
int nameColumn = dataColumns["ColumnName"].Ordinal;
int typeColumn = dataColumns["DataType"].Ordinal;
int ordinalColumn = dataColumns["ColumnOrdinal"].Ordinal;
DataRowCollection schemaRows = schema.Rows;
int schemaRowCount = schemaRows.Count - 1;
//Populates the property mappings
for (int i = schemaRowCount; i != -1; --i)
{
DataRow row = schemaRows[i];
string name = row[nameColumn].ToString();
Type type = Type.GetType(row[typeColumn].ToString());
int ordinal = Convert.ToInt32(row[ordinalColumn]);
propertyMappingsCache.Add(ordinal, new ReaderColumnMap(name, type));
}
//Use expression tree here to create dynamic object (new() { Prop1 = "prop1", Prop2 = prop2... })
//Dictionary sample:
//Id = System.Int64
//Name = System.String
//Birthdate = System.DateTime
//Target Expression:
//dynamic instance = new (){
// Id = (System.Int64)reader[0];
// Name = (System.String)reader[1];
// Birthdate = (System.DateTime)reader[2];
//}
var expressions = new List<Expression>();
var fxParameterExpression = Expression.Parameter(typeof(IDataReader), "reader");
//Compiler error: the typeof operator cannot be used on the dynamic type
var variableExpression = Expression.Variable(typeof(dynamic), "instance"); //dynamic instance = new ()
//Compiler error: the typeof operator cannot be used on the dynamic type
var instantiationExpression = Expression.New(typeof(dynamic));
var assignExpression = Expression.Assign(variableExpression, instantiationExpression);
var indexer = typeof(IDataReader).GetProperty("Item", new[] { typeof(int) }); // []
expressions.Add(assignExpression);
int propertyCount = propertyMappingsCache.Count - 1;
for(int i = propertyCount; i != -1; --i)
{
//Get property details from mappingsCache (string property name and Type of property)
//to create property
}
return null;
}
public dynamic CreateMappedInstance(IDataReader reader)
{
return MappingMethod.Invoke(reader);
}
}
But the compiler is not letting me to instantiate dynamic type: the typeof operator cannot be used on the dynamic type
I plan to call it in sql module like:
public class SqlCaller
{
private readonly ISqlProvider sqlProvider;
public SqlCaller(ISqlProvider sqlProvider)
{
this.sqlProvider = sqlProvider ?? throw new ArgumentNullException("sqlProvider");
}
public DataTable Query(string queryString)...
public DataTable Query(DbCommand command)...
public DataTable GetSchema(string queryString)...
public int ExecuteNonQuery(DbCommand command)...
public int ExecuteNonQuery(string commandString)...
public object ExecuteScalar(string queryString)...
public object ExecuteScalar(DbCommand command)...
public bool ExecuteTransaction(IEnumerable<DbCommand> commands)...
[Obsolete]
public IEnumerable<T> Get<T>(Func<IDataReader, List<T>> mappingMethod, string query)...
[Obsolete]
public IEnumerable<T> Get<T>(Func<IDataReader, List<T>> mappingMethod, DbCommand command)...
public IEnumerable<T> Get<T>(string query) where T : class, new()...
public IEnumerable<T> Get<T>(DbCommand command) where T : class, new()...
public IEnumerable<T> Get<T>(IDataMapper<T> dataMapper, DbCommand command) where T : class, new()...
public IEnumerable<dynamic> GetDynamic(string commandString)
{
return GetDynamic(sqlProvider.CreateCommand(commandString));
}
public IEnumerable<dynamic> GetDynamic(DbCommand command)
{
List<dynamic> temp = new List<dynamic>();
using (DbConnection connection = sqlProvider.CreateConnection())
{
command.Connection = connection;
try
{
command.Connection.Open();
IDataReader reader = command.ExecuteReader();
var watch = Stopwatch.StartNew();
DynamicMapper mapper = new DynamicMapper(reader);
while(reader.Read()) temp.Add(mapper.CreateMappedInstance(reader));
watch.Stop();
}
catch
{
throw;
}
finally
{
command.Connection.Close();
}
}
return temp;
}
}
- How to instantiate a dynamic type inside an expression tree
- How to set property values of a dynamic type inside an expression tree