.Net DataReader映射到实体

aaa

#region Mapper

public static IEnumerable<T> ReaderTo<T>(this IDataReader dataReader)
{
    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();
    var mapper = CreateMappingFunction(dataReader, typeof(T));
    while (dataReader.Read())
    {
        var result = mapper(dataReader as DbDataReader);
        yield return result is T ? (T)result : default(T);
    }
    sw.Stop();
    Console.WriteLine("耗时: {0} 毫秒.", sw.ElapsedMilliseconds);
}

private static Func<DbDataReader, object> CreateMappingFunction(IDataReader reader, Type type)
{
    //1. 取得sql select所有栏位名称
    var names = Enumerable.Range(0, reader.FieldCount).Select(index => reader.GetName(index)).ToArray();

    //2. 取得mapping类别的属性资料 >  将index,sql栏位,class属性资料做好对应封装在一个变量内方便后面使用
    var props = type.GetProperties().ToList();
    var members = names.Select((columnName, index) =>
    {
        var property = props.Find(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase));
        return new
        {
            index,
            columnName,
            property
        };
    }).Where(x => x.property != null);

    //3. 动态建立方法 : 从数据库Reader按照顺序读取我们要的资料
    /*方法逻辑 : 
        User 动态方法(IDataReader reader)
        {
        var user = new User();
        var value = reader[0];
        if( !(value is System.DBNull) )
            user.Name = (string)value;
        value = reader[1];
        if( !(value is System.DBNull) )
            user.Age = (int)value;  
        return user;
        }
    */
    var exBodys = new List<Expression>();

    // 方法(IDataReader reader)
    var exParam = Expression.Parameter(typeof(DbDataReader), "reader");

    // Mapping类别 物件 = new Mapping类别();
    var exVar = Expression.Variable(type, "mappingObj");
    var exNew = Expression.New(type);
    exBodys.Add(Expression.Assign(exVar, exNew));

    // var value = defalut(object);
    var exValueVar = Expression.Variable(typeof(object), "value");
    exBodys.Add(Expression.Assign(exValueVar, Expression.Constant(null)));

    var getItemMethod = typeof(DbDataReader)
        .GetMethods()
        .Where(w => w.Name == "get_Item")
        .First(w => w.GetParameters().First().ParameterType == typeof(int));

    foreach (var m in members)
    {
        if (m.property == null)
            continue;
        //reader[0]
        var exCall = Expression.Call(
            exParam,
            getItemMethod,
            Expression.Constant(m.index)
        );

        // value = reader[0];
        exBodys.Add(Expression.Assign(exValueVar, exCall));

        //user.Name = (string)value;
        var exProp = Expression.Property(exVar, m.property.Name);

        //https://stackoverflow.com/questions/2850265/calling-a-generic-method-using-lambda-expressions-and-a-type-only-known-at-runt
        var exParseCall = Expression.Call(typeof(ExpressionMapper), "Parse", new Type[] { m.property.PropertyType }, exValueVar);               
        var assign = Expression.Assign(exProp, exParseCall);
        var exIfThenElse = Expression.IfThenElse(
                            Expression.TypeIs(exValueVar, typeof(System.DBNull)), Expression.Default(m.property.PropertyType), assign
                        );

        exBodys.Add(exIfThenElse);
    }

    // return user;  
    exBodys.Add(exVar);

    // Compiler Expression 
    var lambda = Expression.Lambda<Func<DbDataReader, object>>(
        Expression.Block(
        new[] { exVar, exValueVar },
        exBodys
        ), exParam
    );

    return lambda.Compile();
}

public static T Parse<T>(object value)
{
    if (value == null || value == DBNull.Value)
        return default(T);

    if (value is T)
        return (T)value;

    var type = typeof(T);
    type = Nullable.GetUnderlyingType(type) ?? type;
    if (type.IsEnum)
    {
        if (value is float || value is double || value is decimal)
        {
            value = Convert.ChangeType(value, Enum.GetUnderlyingType(type), CultureInfo.InvariantCulture);
        }
        return (T)Enum.ToObject(type, value);
    }
    //if (typeHandlers.TryGetValue(type, out ITypeHandler handler))
    //{
    //    return (T)handler.Parse(type, value);
    //}
    return (T)Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
}

#endregion

最后修改于 2023-02-25