Reflection 替换 ExpressionTree,单元测试已通过

This commit is contained in:
28810
2019-01-18 19:17:40 +08:00
parent 863a9ee397
commit 0068474992
16 changed files with 537 additions and 217 deletions

View File

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