如何使用System.Linq.Expressions实例化和设置动态类型的属性

时间:2018-12-11 01:53:31

标签: c# mapping expression-trees

我正在编写一个SQL Data Mapper,它将查询映射到动态类型(我已经经历过通过反射将sql映射到强类型对象)

这次我要进行sql调用,然后返回Ienumerable。

这是我到目前为止所拥有的:

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


}

但是编译器不允许我实例化动态类型:不能在动态类型上使用typeof运算符

我打算在sql模块中调用它,

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;
    }
}
  • 如何在表达式树中实例化动态类型
  • 如何在表达式树内设置动态类型的属性值

0 个答案:

没有答案