mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-06-20 12:58:15 +08:00
Reflection 替换 ExpressionTree,单元测试已通过
This commit is contained in:
@ -7,6 +7,8 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
@ -111,7 +113,7 @@ namespace FreeSql.Internal {
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
static Dictionary<Type, bool> dicExecuteArrayRowReadClassOrTuple = new Dictionary<Type, bool> {
|
||||
internal static Dictionary<Type, bool> dicExecuteArrayRowReadClassOrTuple = new Dictionary<Type, bool> {
|
||||
[typeof(bool)] = true,
|
||||
[typeof(sbyte)] = true,
|
||||
[typeof(short)] = true,
|
||||
@ -164,7 +166,7 @@ namespace FreeSql.Internal {
|
||||
[typeof(JObject)] = true,
|
||||
[typeof(JArray)] = true,
|
||||
};
|
||||
static ConcurrentDictionary<Type, Func<Type, Dictionary<string, int>, object[], int, RowInfo>> _dicExecuteArrayRowReadClassOrTuple = new ConcurrentDictionary<Type, Func<Type, Dictionary<string, int>, object[], int, RowInfo>>();
|
||||
static ConcurrentDictionary<Type, Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>> _dicExecuteArrayRowReadClassOrTuple = new ConcurrentDictionary<Type, Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>();
|
||||
internal class RowInfo {
|
||||
public object Value { get; set; }
|
||||
public int DataIndex { get; set; }
|
||||
@ -176,17 +178,18 @@ namespace FreeSql.Internal {
|
||||
public static PropertyInfo PropertyValue = typeof(RowInfo).GetProperty("Value");
|
||||
public static PropertyInfo PropertyDataIndex = typeof(RowInfo).GetProperty("DataIndex");
|
||||
}
|
||||
internal static RowInfo ExecuteArrayRowReadClassOrTuple(Type type, Dictionary<string, int> names, object[] row, int dataIndex = 0) {
|
||||
internal static MethodInfo MethodDataReaderGetValue = typeof(DbDataReader).GetMethod("GetValue");
|
||||
internal static RowInfo ExecuteArrayRowReadClassOrTuple(Type type, Dictionary<string, int> names, DbDataReader row, int dataIndex = 0) {
|
||||
var func = _dicExecuteArrayRowReadClassOrTuple.GetOrAdd(type, s => {
|
||||
var returnTarget = Expression.Label(typeof(RowInfo));
|
||||
var typeExp = Expression.Parameter(typeof(Type), "type");
|
||||
var namesExp = Expression.Parameter(typeof(Dictionary<string, int>), "names");
|
||||
var rowExp = Expression.Parameter(typeof(object[]), "row");
|
||||
var rowExp = Expression.Parameter(typeof(DbDataReader), "row");
|
||||
var dataIndexExp = Expression.Parameter(typeof(int), "dataIndex");
|
||||
|
||||
if (type.IsArray) return Expression.Lambda<Func<Type, Dictionary<string, int>, object[], int, RowInfo>>(
|
||||
if (type.IsArray) return Expression.Lambda<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(
|
||||
Expression.New(RowInfo.Constructor,
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.ArrayAccess(rowExp, dataIndexExp) }),
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }),
|
||||
Expression.Add(dataIndexExp, Expression.Constant(1))
|
||||
), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile();
|
||||
|
||||
@ -194,9 +197,9 @@ namespace FreeSql.Internal {
|
||||
if (typeGeneric.FullName.StartsWith("System.Nullable`1[")) typeGeneric = type.GenericTypeArguments.First();
|
||||
if (typeGeneric.IsEnum ||
|
||||
dicExecuteArrayRowReadClassOrTuple.ContainsKey(typeGeneric))
|
||||
return Expression.Lambda<Func<Type, Dictionary<string, int>, object[], int, RowInfo>>(
|
||||
return Expression.Lambda<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(
|
||||
Expression.New(RowInfo.Constructor,
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.ArrayAccess(rowExp, dataIndexExp) }),
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }),
|
||||
Expression.Add(dataIndexExp, Expression.Constant(1))
|
||||
), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile();
|
||||
|
||||
@ -211,13 +214,32 @@ namespace FreeSql.Internal {
|
||||
|
||||
var fields = type.GetFields();
|
||||
foreach (var field in fields) {
|
||||
Expression read2ExpAssign = null; //加速缓存
|
||||
if (field.FieldType.IsArray) read2ExpAssign = Expression.New(RowInfo.Constructor,
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(field.FieldType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }),
|
||||
Expression.Add(dataIndexExp, Expression.Constant(1))
|
||||
);
|
||||
else {
|
||||
var fieldtypeGeneric = field.FieldType;
|
||||
if (fieldtypeGeneric.FullName.StartsWith("System.Nullable`1[")) fieldtypeGeneric = fieldtypeGeneric.GenericTypeArguments.First();
|
||||
if (fieldtypeGeneric.IsEnum ||
|
||||
dicExecuteArrayRowReadClassOrTuple.ContainsKey(fieldtypeGeneric)) read2ExpAssign = Expression.New(RowInfo.Constructor,
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(field.FieldType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }),
|
||||
Expression.Add(dataIndexExp, Expression.Constant(1))
|
||||
);
|
||||
else {
|
||||
read2ExpAssign = Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(field.FieldType), namesExp, rowExp, dataIndexExp });
|
||||
}
|
||||
}
|
||||
block2Exp.AddRange(new Expression[] {
|
||||
//Expression.TryCatch(Expression.Block(
|
||||
// typeof(void),
|
||||
Expression.Assign(read2Exp, Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(field.FieldType), namesExp, rowExp, dataIndexExp })),
|
||||
Expression.Assign(read2Exp, read2ExpAssign),
|
||||
Expression.IfThen(Expression.GreaterThan(read2ExpDataIndex, dataIndexExp),
|
||||
Expression.Assign(dataIndexExp, read2ExpDataIndex)),
|
||||
Expression.Assign(Expression.MakeMemberAccess(ret2Exp, field), Expression.Convert(read2ExpValue, field.FieldType))
|
||||
Expression.IfThenElse(Expression.Equal(read2ExpValue, Expression.Constant(null)),
|
||||
Expression.Assign(Expression.MakeMemberAccess(ret2Exp, field), Expression.Default(field.FieldType)),
|
||||
Expression.Assign(Expression.MakeMemberAccess(ret2Exp, field), Expression.Convert(read2ExpValue, field.FieldType)))
|
||||
//),
|
||||
//Expression.Catch(typeof(Exception), Expression.Block(
|
||||
// Expression.IfThen(Expression.Equal(read2ExpDataIndex, Expression.Constant(0)), Expression.Throw(Expression.Constant(new Exception(field.Name + "," + 0)))),
|
||||
@ -233,16 +255,16 @@ namespace FreeSql.Internal {
|
||||
Expression.Return(returnTarget, Expression.New(RowInfo.Constructor, Expression.Convert(ret2Exp, typeof(object)), dataIndexExp)),
|
||||
Expression.Label(returnTarget, Expression.Default(typeof(RowInfo)))
|
||||
});
|
||||
return Expression.Lambda<Func<Type, Dictionary<string, int>, object[], int, RowInfo>>(
|
||||
return Expression.Lambda<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(
|
||||
Expression.Block(new[] { ret2Exp, read2Exp }, block2Exp), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile();
|
||||
}
|
||||
var rowLenExp = Expression.ArrayLength(rowExp);
|
||||
return Expression.Lambda<Func<Type, Dictionary<string, int>, object[], int, RowInfo>>(
|
||||
return Expression.Lambda<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(
|
||||
Expression.Block(
|
||||
Expression.IfThen(
|
||||
Expression.LessThan(dataIndexExp, rowLenExp),
|
||||
Expression.Return(returnTarget, Expression.New(RowInfo.Constructor,
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.ArrayAccess(rowExp, dataIndexExp) }),
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }),
|
||||
Expression.Add(dataIndexExp, Expression.Constant(1))))
|
||||
),
|
||||
Expression.Label(returnTarget, Expression.Default(typeof(RowInfo)))
|
||||
@ -250,14 +272,14 @@ namespace FreeSql.Internal {
|
||||
}
|
||||
|
||||
if (type == typeof(object) && names != null) {
|
||||
Func<Type, Dictionary<string, int>, object[], int, RowInfo> dynamicFunc = (type2, names2, row2, dataindex2) => {
|
||||
Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo> dynamicFunc = (type2, names2, row2, dataindex2) => {
|
||||
dynamic expando = new System.Dynamic.ExpandoObject(); //动态类型字段 可读可写
|
||||
var expandodic = (IDictionary<string, object>)expando;
|
||||
foreach (var name in names2)
|
||||
expandodic.Add(name.Key, row2[name.Value]);
|
||||
expandodic.Add(name.Key, row2.GetValue(name.Value));
|
||||
return new RowInfo(expando, names2.Count);
|
||||
};
|
||||
return dynamicFunc;// Expression.Lambda<Func<Type, Dictionary<string, int>, object[], int, RowInfo>>(null);
|
||||
return dynamicFunc;// Expression.Lambda<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(null);
|
||||
}
|
||||
|
||||
//类注入属性
|
||||
@ -265,42 +287,108 @@ namespace FreeSql.Internal {
|
||||
var readExp = Expression.Variable(typeof(RowInfo), "read");
|
||||
var readExpValue = Expression.MakeMemberAccess(readExp, RowInfo.PropertyValue);
|
||||
var readExpDataIndex = Expression.MakeMemberAccess(readExp, RowInfo.PropertyDataIndex);
|
||||
var readExpValueParms = new List<ParameterExpression>();
|
||||
var readExpsIndex = Expression.Variable(typeof(int), "readsIndex");
|
||||
var tryidxExp = Expression.Variable(typeof(int), "tryidx");
|
||||
var blockExp = new List<Expression>();
|
||||
blockExp.Add(Expression.Assign(retExp, Expression.New(type.GetConstructor(new Type[0]))));
|
||||
|
||||
var props = type.GetProperties();
|
||||
foreach (var prop in props) {
|
||||
var propGetSetMethod = prop.GetSetMethod();
|
||||
blockExp.AddRange(new Expression[] {
|
||||
Expression.Assign(tryidxExp, dataIndexExp),
|
||||
Expression.IfThen(Expression.Not(Expression.And(
|
||||
Expression.NotEqual(namesExp, Expression.Constant(null)),
|
||||
Expression.Not(Expression.Call(namesExp, namesExp.Type.GetMethod("TryGetValue"), Expression.Constant(prop.Name), tryidxExp)))),
|
||||
Expression.Block(
|
||||
//Expression.Assign(tryidxExp, Expression.Call(namesExp, namesExp Expression.Constant(prop.Name))),
|
||||
Expression.Assign(readExp, Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(prop.PropertyType), namesExp, rowExp, tryidxExp })),
|
||||
Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp),
|
||||
Expression.Assign(dataIndexExp, readExpDataIndex)),
|
||||
Expression.IfThen(Expression.NotEqual(readExpValue, Expression.Constant(null)),
|
||||
Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType)))
|
||||
)
|
||||
)
|
||||
});
|
||||
var ctor = type.GetConstructor(new Type[0]) ?? type.GetConstructors().First();
|
||||
var ctorParms = ctor.GetParameters();
|
||||
if (ctorParms.Length > 0) {
|
||||
foreach(var ctorParm in ctorParms) {
|
||||
Expression readExpAssign = null; //加速缓存
|
||||
if (ctorParm.ParameterType.IsArray) readExpAssign = Expression.New(RowInfo.Constructor,
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(ctorParm.ParameterType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }),
|
||||
Expression.Add(dataIndexExp, Expression.Constant(1))
|
||||
);
|
||||
else {
|
||||
var proptypeGeneric = ctorParm.ParameterType;
|
||||
if (proptypeGeneric.FullName.StartsWith("System.Nullable`1[")) proptypeGeneric = proptypeGeneric.GenericTypeArguments.First();
|
||||
if (proptypeGeneric.IsEnum ||
|
||||
dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) readExpAssign = Expression.New(RowInfo.Constructor,
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(ctorParm.ParameterType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }),
|
||||
Expression.Add(dataIndexExp, Expression.Constant(1))
|
||||
);
|
||||
else {
|
||||
readExpAssign = Expression.New(RowInfo.Constructor,
|
||||
Expression.MakeMemberAccess(Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(ctorParm.ParameterType), namesExp, rowExp, dataIndexExp }), RowInfo.PropertyValue),
|
||||
Expression.Add(dataIndexExp, Expression.Constant(1)));
|
||||
}
|
||||
}
|
||||
var varctorParm = Expression.Variable(ctorParm.ParameterType, $"ctorParm{ctorParm.Name}");
|
||||
readExpValueParms.Add(varctorParm);
|
||||
blockExp.AddRange(new Expression[] {
|
||||
Expression.Assign(tryidxExp, dataIndexExp),
|
||||
//以下注释部分为【严格读取】,会损失一点性能
|
||||
//Expression.IfThen(Expression.Not(Expression.And(
|
||||
// Expression.NotEqual(namesExp, Expression.Constant(null)),
|
||||
// Expression.Not(Expression.Call(namesExp, namesExp.Type.GetMethod("TryGetValue"), Expression.Constant(prop.Name), tryidxExp)))),
|
||||
// Expression.Block(
|
||||
Expression.Assign(readExp, readExpAssign),
|
||||
Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp),
|
||||
Expression.Assign(dataIndexExp, readExpDataIndex)),
|
||||
Expression.IfThenElse(Expression.Equal(readExpValue, Expression.Constant(null)),
|
||||
Expression.Assign(varctorParm, Expression.Default(ctorParm.ParameterType)),
|
||||
Expression.Assign(varctorParm, Expression.Convert(readExpValue, ctorParm.ParameterType)))
|
||||
// )
|
||||
//)
|
||||
});
|
||||
}
|
||||
blockExp.Add(Expression.Assign(retExp, Expression.New(ctor, readExpValueParms)));
|
||||
} else {
|
||||
blockExp.Add(Expression.Assign(retExp, Expression.New(ctor)));
|
||||
|
||||
var props = type.GetProperties();
|
||||
foreach (var prop in props) {
|
||||
var propGetSetMethod = prop.GetSetMethod();
|
||||
Expression readExpAssign = null; //加速缓存
|
||||
if (prop.PropertyType.IsArray) readExpAssign = Expression.New(RowInfo.Constructor,
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }),
|
||||
Expression.Add(dataIndexExp, Expression.Constant(1))
|
||||
);
|
||||
else {
|
||||
var proptypeGeneric = prop.PropertyType;
|
||||
if (proptypeGeneric.FullName.StartsWith("System.Nullable`1[")) proptypeGeneric = proptypeGeneric.GenericTypeArguments.First();
|
||||
if (proptypeGeneric.IsEnum ||
|
||||
dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) readExpAssign = Expression.New(RowInfo.Constructor,
|
||||
Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }),
|
||||
Expression.Add(dataIndexExp, Expression.Constant(1))
|
||||
);
|
||||
else {
|
||||
continue;
|
||||
readExpAssign = Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(prop.PropertyType), namesExp, rowExp, tryidxExp });
|
||||
}
|
||||
}
|
||||
blockExp.AddRange(new Expression[] {
|
||||
Expression.Assign(tryidxExp, dataIndexExp),
|
||||
//以下注释部分为【严格读取】,会损失一点性能
|
||||
//Expression.IfThen(Expression.Not(Expression.And(
|
||||
// Expression.NotEqual(namesExp, Expression.Constant(null)),
|
||||
// Expression.Not(Expression.Call(namesExp, namesExp.Type.GetMethod("TryGetValue"), Expression.Constant(prop.Name), tryidxExp)))),
|
||||
// Expression.Block(
|
||||
Expression.Assign(readExp, readExpAssign),
|
||||
Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp),
|
||||
Expression.Assign(dataIndexExp, readExpDataIndex)),
|
||||
Expression.IfThenElse(Expression.Equal(readExpValue, Expression.Constant(null)),
|
||||
Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)),
|
||||
Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType)))
|
||||
// )
|
||||
//)
|
||||
});
|
||||
}
|
||||
}
|
||||
blockExp.AddRange(new Expression[] {
|
||||
Expression.Return(returnTarget, Expression.New(RowInfo.Constructor, retExp, dataIndexExp)),
|
||||
Expression.Label(returnTarget, Expression.Default(typeof(RowInfo)))
|
||||
});
|
||||
return Expression.Lambda<Func<Type, Dictionary<string, int>, object[], int, RowInfo>>(
|
||||
Expression.Block(new[] { retExp, readExp, tryidxExp }, blockExp), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile();
|
||||
return Expression.Lambda<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(
|
||||
Expression.Block(new[] { retExp, readExp, tryidxExp, readExpsIndex }.Concat(readExpValueParms), blockExp), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile();
|
||||
});
|
||||
|
||||
return func(type, names, row, dataIndex);
|
||||
}
|
||||
|
||||
static MethodInfo MethodExecuteArrayRowReadClassOrTuple = typeof(Utils).GetMethod("ExecuteArrayRowReadClassOrTuple", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
static MethodInfo MethodGetDataReaderValue = typeof(Utils).GetMethod("GetDataReaderValue", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
internal static MethodInfo MethodExecuteArrayRowReadClassOrTuple = typeof(Utils).GetMethod("ExecuteArrayRowReadClassOrTuple", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
internal static MethodInfo MethodGetDataReaderValue = typeof(Utils).GetMethod("GetDataReaderValue", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
static ConcurrentDictionary<string, Action<object, object>> _dicFillPropertyValue = new ConcurrentDictionary<string, Action<object, object>>();
|
||||
internal static void FillPropertyValue(object info, string memberAccessPath, object value) {
|
||||
@ -322,11 +410,19 @@ namespace FreeSql.Internal {
|
||||
act(info, value);
|
||||
}
|
||||
|
||||
static ConcurrentDictionary<Type, Func<object, object>> _dicGetDataReaderValue = new ConcurrentDictionary<Type, Func<object, object>>();
|
||||
static ConcurrentDictionary<Type, ConcurrentDictionary<Type, Func<object, object>>> _dicGetDataReaderValue = new ConcurrentDictionary<Type, ConcurrentDictionary<Type, Func<object, object>>>();
|
||||
static MethodInfo MethodArrayGetValue = typeof(Array).GetMethod("GetValue", new[] { typeof(int) });
|
||||
static MethodInfo MethodMygisGeometryParse = typeof(MygisGeometry).GetMethod("Parse", new[] { typeof(string) });
|
||||
static MethodInfo MethodGuidParse = typeof(Guid).GetMethod("Parse", new[] { typeof(string) });
|
||||
static MethodInfo MethodEnumParse = typeof(Enum).GetMethod("Parse", new[] { typeof(Type), typeof(string), typeof(bool) });
|
||||
static MethodInfo MethodToString = typeof(string).GetMethod("Concat", new[] { typeof(object) });
|
||||
static MethodInfo MethodConvertChangeType = typeof(Convert).GetMethod("ChangeType", new[] { typeof(object), typeof(Type) });
|
||||
static MethodInfo MethodTimeSpanFromSeconds = typeof(TimeSpan).GetMethod("FromSeconds");
|
||||
static MethodInfo MethodDoubleParse = typeof(double).GetMethod("Parse", new[] { typeof(string) });
|
||||
internal static object GetDataReaderValue(Type type, object value) {
|
||||
if (value == null || value == DBNull.Value) return null;
|
||||
|
||||
var func = _dicGetDataReaderValue.GetOrAdd(type, k => {
|
||||
var func = _dicGetDataReaderValue.GetOrAdd(type, k1 => new ConcurrentDictionary<Type, Func<object, object>>()).GetOrAdd(value.GetType(), valueType => {
|
||||
var returnTarget = Expression.Label(typeof(object));
|
||||
var parmExp = Expression.Parameter(typeof(object), "value");
|
||||
|
||||
@ -334,94 +430,90 @@ namespace FreeSql.Internal {
|
||||
|
||||
if (type.IsArray) {
|
||||
var elementType = type.GetElementType();
|
||||
var valueArr = value as Array;
|
||||
if (elementType == valueType.GetElementType()) return Expression.Lambda<Func<object, object>>(parmExp, parmExp).Compile();
|
||||
|
||||
if (elementType == valueArr.GetType().GetElementType()) return Expression.Lambda<Func<object, object>>(parmExp, parmExp).Compile();
|
||||
|
||||
var arr = Expression.Variable(type, "arr");
|
||||
var ret = Expression.Variable(type, "ret");
|
||||
var arr = Expression.Variable(valueType, "arr");
|
||||
var arrlen = Expression.Variable(typeof(int), "arrlen");
|
||||
var x = Expression.Variable(typeof(int), "x");
|
||||
var readval = Expression.Variable(typeof(object), "readval");
|
||||
var label = Expression.Label(typeof(int));
|
||||
var ret = Expression.NewArrayBounds(elementType, arrlen);
|
||||
return Expression.Lambda<Func<object, object>>(
|
||||
Expression.Block(
|
||||
new[] { arr, arrlen, x },
|
||||
Expression.Assign(arr, Expression.Convert(parmExp, type)),
|
||||
new[] { ret, arr, arrlen, readval, x },
|
||||
Expression.Assign(arr, Expression.TypeAs(parmExp, valueType)),
|
||||
Expression.Assign(arrlen, Expression.ArrayLength(arr)),
|
||||
Expression.Assign(x, Expression.Constant(0)),
|
||||
ret,
|
||||
Expression.Assign(ret, Expression.NewArrayBounds(elementType, arrlen)),
|
||||
Expression.Loop(
|
||||
Expression.IfThenElse(
|
||||
Expression.LessThan(x, arrlen),
|
||||
Expression.Block(
|
||||
Expression.Assign(
|
||||
Expression.ArrayAccess(ret, x),
|
||||
Expression.Call(
|
||||
MethodGetDataReaderValue,
|
||||
Expression.Constant(elementType, typeof(Type)),
|
||||
Expression.ArrayAccess(arr, x)
|
||||
)
|
||||
Expression.Assign(readval, Expression.Call(
|
||||
MethodGetDataReaderValue,
|
||||
Expression.Constant(elementType, typeof(Type)),
|
||||
Expression.Convert(Expression.ArrayAccess(arr, x), typeof(object))
|
||||
)),
|
||||
Expression.IfThenElse(
|
||||
Expression.Equal(readval, Expression.Constant(null)),
|
||||
Expression.Assign(Expression.ArrayAccess(ret, x), Expression.Default(elementType)),
|
||||
Expression.Assign(Expression.ArrayAccess(ret, x), Expression.Convert(readval, elementType))
|
||||
),
|
||||
Expression.PostIncrementAssign(x)
|
||||
),
|
||||
Expression.Break(label, x)
|
||||
),
|
||||
label
|
||||
)
|
||||
),
|
||||
Expression.Return(returnTarget, ret),
|
||||
Expression.Label(returnTarget, Expression.Default(typeof(object)))
|
||||
), parmExp).Compile();
|
||||
}
|
||||
|
||||
if (type.FullName.StartsWith("System.Nullable`1[")) type = type.GenericTypeArguments.First();
|
||||
if (type.IsEnum) return Expression.Lambda<Func<object, object>>(
|
||||
Expression.Call(
|
||||
typeof(Enum).GetMethod("Parse", new[] { typeof(Type), typeof(string), typeof(bool) }),
|
||||
MethodEnumParse,
|
||||
Expression.Constant(type, typeof(Type)),
|
||||
Expression.Convert(parmExp, typeof(string)),
|
||||
Expression.Call(MethodToString, parmExp),
|
||||
Expression.Constant(true, typeof(bool))
|
||||
) , parmExp).Compile();
|
||||
|
||||
switch (type.FullName) {
|
||||
case "System.Guid":
|
||||
if (value.GetType() != type) return Expression.Lambda<Func<object, object>>(
|
||||
Expression.Block(
|
||||
Expression.TryCatch(
|
||||
Expression.Return(returnTarget, Expression.Call(typeof(Guid).GetMethod("Parse"), Expression.Convert(parmExp, typeof(string)))),
|
||||
Expression.Catch(typeof(Exception),
|
||||
Expression.Return(returnTarget, Expression.Constant(Guid.Empty)))
|
||||
),
|
||||
Expression.Label(returnTarget, Expression.Default(type))
|
||||
), parmExp).Compile();
|
||||
|
||||
if (valueType != type) return Expression.Lambda<Func<object, object>>(
|
||||
Expression.Convert(Expression.Call(MethodGuidParse, Expression.Convert(parmExp, typeof(string))), typeof(object))
|
||||
, parmExp).Compile();
|
||||
return Expression.Lambda<Func<object, object>>(parmExp, parmExp).Compile();
|
||||
|
||||
case "MygisPoint": return Expression.Lambda<Func<object, object>>(
|
||||
Expression.TypeAs(
|
||||
Expression.Call(typeof(MygisPoint).GetMethod("Parse"), Expression.Convert(parmExp, typeof(string))),
|
||||
Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))),
|
||||
typeof(MygisPoint)
|
||||
), parmExp).Compile();
|
||||
case "MygisLineString": return Expression.Lambda<Func<object, object>>(
|
||||
Expression.TypeAs(
|
||||
Expression.Call(typeof(MygisLineString).GetMethod("Parse"), Expression.Convert(parmExp, typeof(string))),
|
||||
Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))),
|
||||
typeof(MygisLineString)
|
||||
), parmExp).Compile();
|
||||
case "MygisPolygon": return Expression.Lambda<Func<object, object>>(
|
||||
Expression.TypeAs(
|
||||
Expression.Call(typeof(MygisPolygon).GetMethod("Parse"), Expression.Convert(parmExp, typeof(string))),
|
||||
Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))),
|
||||
typeof(MygisPolygon)
|
||||
), parmExp).Compile();
|
||||
case "MygisMultiPoint": return Expression.Lambda<Func<object, object>>(
|
||||
Expression.TypeAs(
|
||||
Expression.Call(typeof(MygisMultiPoint).GetMethod("Parse"), Expression.Convert(parmExp, typeof(string))),
|
||||
Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))),
|
||||
typeof(MygisMultiPoint)
|
||||
), parmExp).Compile();
|
||||
case "MygisMultiLineString": return Expression.Lambda<Func<object, object>>(
|
||||
Expression.TypeAs(
|
||||
Expression.Call(typeof(MygisMultiLineString).GetMethod("Parse"), Expression.Convert(parmExp, typeof(string))),
|
||||
Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))),
|
||||
typeof(MygisMultiLineString)
|
||||
), parmExp).Compile();
|
||||
case "MygisMultiPolygon": return Expression.Lambda<Func<object, object>>(
|
||||
Expression.TypeAs(
|
||||
Expression.Call(typeof(MygisMultiPolygon).GetMethod("Parse"), Expression.Convert(parmExp, typeof(string))),
|
||||
Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))),
|
||||
typeof(MygisMultiPolygon)
|
||||
), parmExp).Compile();
|
||||
case "Newtonsoft.Json.Linq.JToken": return Expression.Lambda<Func<object, object>>(
|
||||
@ -441,20 +533,58 @@ namespace FreeSql.Internal {
|
||||
), parmExp).Compile();
|
||||
case "Npgsql.LegacyPostgis.PostgisGeometry": return Expression.Lambda<Func<object, object>>(parmExp, parmExp).Compile();
|
||||
}
|
||||
if (type != value.GetType()) {
|
||||
if (type != valueType) {
|
||||
if (type.FullName == "System.TimeSpan") return Expression.Lambda<Func<object, object>>(
|
||||
Expression.Call(
|
||||
typeof(TimeSpan).GetMethod("FromSeconds"),
|
||||
Expression.Call(typeof(double).GetMethod("Parse", new[] { typeof(string) }), Expression.Convert(parmExp, typeof(string)))
|
||||
), parmExp).Compile();
|
||||
Expression.Convert(Expression.Call(
|
||||
MethodTimeSpanFromSeconds,
|
||||
Expression.Call(MethodDoubleParse, Expression.Call(MethodToString, parmExp))
|
||||
), typeof(object)), parmExp).Compile();
|
||||
return Expression.Lambda<Func<object, object>>(
|
||||
Expression.Call(typeof(Convert).GetMethod("ChangeType", new[] { typeof(object), typeof(Type) }), parmExp, Expression.Constant(type, typeof(Type)))
|
||||
Expression.Call(MethodConvertChangeType, parmExp, Expression.Constant(type, typeof(Type)))
|
||||
, parmExp).Compile();
|
||||
}
|
||||
return Expression.Lambda<Func<object, object>>(parmExp, parmExp).Compile();
|
||||
});
|
||||
return func(value);
|
||||
}
|
||||
internal static object GetDataReaderValue22(Type type, object value) {
|
||||
if (value == null || value == DBNull.Value) return null;
|
||||
if (type.FullName == "System.Byte[]") return value;
|
||||
if (type.IsArray) {
|
||||
var elementType = type.GetElementType();
|
||||
var valueArr = value as Array;
|
||||
if (elementType == valueArr.GetType().GetElementType()) return value;
|
||||
var len = valueArr.GetLength(0);
|
||||
var ret = Array.CreateInstance(elementType, len);
|
||||
for (var a = 0; a < len; a++) {
|
||||
var item = valueArr.GetValue(a);
|
||||
ret.SetValue(GetDataReaderValue(elementType, item), a);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
if (type.FullName.StartsWith("System.Nullable`1[")) type = type.GenericTypeArguments.First();
|
||||
if (type.IsEnum) return Enum.Parse(type, string.Concat(value), true);
|
||||
switch (type.FullName) {
|
||||
case "System.Guid":
|
||||
if (value.GetType() != type) return Guid.TryParse(string.Concat(value), out var tryguid) ? tryguid : Guid.Empty;
|
||||
return value;
|
||||
case "MygisPoint": return MygisPoint.Parse(string.Concat(value)) as MygisPoint;
|
||||
case "MygisLineString": return MygisLineString.Parse(string.Concat(value)) as MygisLineString;
|
||||
case "MygisPolygon": return MygisPolygon.Parse(string.Concat(value)) as MygisPolygon;
|
||||
case "MygisMultiPoint": return MygisMultiPoint.Parse(string.Concat(value)) as MygisMultiPoint;
|
||||
case "MygisMultiLineString": return MygisMultiLineString.Parse(string.Concat(value)) as MygisMultiLineString;
|
||||
case "MygisMultiPolygon": return MygisMultiPolygon.Parse(string.Concat(value)) as MygisMultiPolygon;
|
||||
case "Newtonsoft.Json.Linq.JToken": return JToken.Parse(string.Concat(value));
|
||||
case "Newtonsoft.Json.Linq.JObject": return JObject.Parse(string.Concat(value));
|
||||
case "Newtonsoft.Json.Linq.JArray": return JArray.Parse(string.Concat(value));
|
||||
case "Npgsql.LegacyPostgis.PostgisGeometry": return value;
|
||||
}
|
||||
if (type != value.GetType()) {
|
||||
if (type.FullName == "System.TimeSpan") return TimeSpan.FromSeconds(double.Parse(value.ToString()));
|
||||
return Convert.ChangeType(value, type);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
internal static string GetCsName(string name) {
|
||||
name = Regex.Replace(name.TrimStart('@'), @"[^\w]", "_");
|
||||
return char.IsLetter(name, 0) ? name : string.Concat("_", name);
|
||||
|
Reference in New Issue
Block a user