1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
| #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
|