mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 02:32:50 +08:00
ExpreessTree替代反射缓存(1)
This commit is contained in:
parent
ed239835c6
commit
863a9ee397
@ -20,7 +20,7 @@ namespace FreeSql.Tests.PerformanceTest {
|
|||||||
time.Restart();
|
time.Restart();
|
||||||
List<xxx> dplist1 = null;
|
List<xxx> dplist1 = null;
|
||||||
using (var conn = g.mysql.Ado.MasterPool.Get()) {
|
using (var conn = g.mysql.Ado.MasterPool.Get()) {
|
||||||
dplist1 = Dapper.SqlMapper.Query<xxx>(conn.Value, "select * from song limit 1").ToList();
|
dplist1 = Dapper.SqlMapper.Query<xxx>(conn.Value, "select * from song").ToList();
|
||||||
}
|
}
|
||||||
time.Stop();
|
time.Stop();
|
||||||
sb.AppendLine($"Elapsed: {time.Elapsed}; Query Entity Counts: {dplist1.Count}; ORM: Dapper");
|
sb.AppendLine($"Elapsed: {time.Elapsed}; Query Entity Counts: {dplist1.Count}; ORM: Dapper");
|
||||||
@ -44,6 +44,8 @@ namespace FreeSql.Tests.PerformanceTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var t31 = g.mysql.Ado.Query<xxx>("select * from song limit 1");
|
||||||
|
|
||||||
time.Restart();
|
time.Restart();
|
||||||
var t3 = g.mysql.Ado.Query<xxx>("select * from song");
|
var t3 = g.mysql.Ado.Query<xxx>("select * from song");
|
||||||
time.Stop();
|
time.Stop();
|
||||||
@ -130,6 +132,13 @@ namespace FreeSql.Tests.PerformanceTest {
|
|||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
var time = new Stopwatch();
|
var time = new Stopwatch();
|
||||||
|
|
||||||
|
//var t31 = g.mysql.Select<xxx>().ToList();
|
||||||
|
|
||||||
|
time.Restart();
|
||||||
|
var t3 = g.mysql.Select<xxx>().ToList();
|
||||||
|
time.Stop();
|
||||||
|
sb.AppendLine($"Elapsed: {time.Elapsed}; ToList Entity Counts: {t3.Count}; ORM: FreeSql*");
|
||||||
|
|
||||||
time.Restart();
|
time.Restart();
|
||||||
List<xxx> dplist1 = null;
|
List<xxx> dplist1 = null;
|
||||||
using (var conn = g.mysql.Ado.MasterPool.Get()) {
|
using (var conn = g.mysql.Ado.MasterPool.Get()) {
|
||||||
@ -137,12 +146,6 @@ namespace FreeSql.Tests.PerformanceTest {
|
|||||||
}
|
}
|
||||||
time.Stop();
|
time.Stop();
|
||||||
sb.AppendLine($"Elapsed: {time.Elapsed}; Query Entity Counts: {dplist1.Count}; ORM: Dapper");
|
sb.AppendLine($"Elapsed: {time.Elapsed}; Query Entity Counts: {dplist1.Count}; ORM: Dapper");
|
||||||
|
|
||||||
|
|
||||||
time.Restart();
|
|
||||||
var t3 = g.mysql.Select<xxx>().ToList();
|
|
||||||
time.Stop();
|
|
||||||
sb.AppendLine($"Elapsed: {time.Elapsed}; ToList Entity Counts: {t3.Count}; ORM: FreeSql*");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -150,6 +153,19 @@ namespace FreeSql.Tests.PerformanceTest {
|
|||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
var time = new Stopwatch();
|
var time = new Stopwatch();
|
||||||
|
|
||||||
|
time.Restart();
|
||||||
|
var t3Count = 0;
|
||||||
|
var p3 = Parallel.For(1, 50, b => {
|
||||||
|
List<xxx> t3 = new List<xxx>();
|
||||||
|
for (var a = 0; a < 1000; a++) {
|
||||||
|
t3.AddRange(g.mysql.Select<xxx>().Limit(50).ToList());
|
||||||
|
}
|
||||||
|
Interlocked.Add(ref t3Count, t3.Count);
|
||||||
|
});
|
||||||
|
while (p3.IsCompleted == false) ;
|
||||||
|
time.Stop();
|
||||||
|
sb.AppendLine($"Elapsed: {time.Elapsed}; ToList Entity Counts: {t3Count}; ORM: FreeSql*");
|
||||||
|
|
||||||
time.Restart();
|
time.Restart();
|
||||||
var dplist1Count = 0;
|
var dplist1Count = 0;
|
||||||
var p1 = Parallel.For(1, 50, b => {
|
var p1 = Parallel.For(1, 50, b => {
|
||||||
@ -159,25 +175,11 @@ namespace FreeSql.Tests.PerformanceTest {
|
|||||||
dplist1.AddRange(Dapper.SqlMapper.Query<xxx>(conn.Value, "select * from song limit 50").ToList());
|
dplist1.AddRange(Dapper.SqlMapper.Query<xxx>(conn.Value, "select * from song limit 50").ToList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Interlocked.Exchange(ref dplist1Count, dplist1.Count);
|
Interlocked.Add(ref dplist1Count, dplist1.Count);
|
||||||
});
|
});
|
||||||
while (p1.IsCompleted == false) ;
|
while (p1.IsCompleted == false) ;
|
||||||
time.Stop();
|
time.Stop();
|
||||||
sb.AppendLine($"Elapsed: {time.Elapsed}; Query Entity Counts: {dplist1Count}; ORM: Dapper");
|
sb.AppendLine($"Elapsed: {time.Elapsed}; Query Entity Counts: {dplist1Count}; ORM: Dapper");
|
||||||
|
|
||||||
|
|
||||||
time.Restart();
|
|
||||||
var t3Count = 0;
|
|
||||||
var p3 = Parallel.For(1, 50, b => {
|
|
||||||
List<xxx> t3 = new List<xxx>();
|
|
||||||
for (var a = 0; a < 1000; a++) {
|
|
||||||
t3.AddRange(g.mysql.Select<xxx>().Limit(50).ToList());
|
|
||||||
}
|
|
||||||
Interlocked.Exchange(ref t3Count, t3.Count);
|
|
||||||
});
|
|
||||||
while (p3.IsCompleted == false) ;
|
|
||||||
time.Stop();
|
|
||||||
sb.AppendLine($"Elapsed: {time.Elapsed}; ToList Entity Counts: {t3Count}; ORM: FreeSql*");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Table(Name = "song")]
|
[Table(Name = "song")]
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<Version>0.0.6</Version>
|
<Version>0.0.7</Version>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<Authors>YeXiangQin</Authors>
|
<Authors>YeXiangQin</Authors>
|
||||||
<Description>打造 .NETCore 最方便的 ORM,DbFirst 与 CodeFirst 混合使用,提供从实体同步数据库,或者从数据库生成实体代码,支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite 数据库。</Description>
|
<Description>打造 .NETCore 最方便的 ORM,DbFirst 与 CodeFirst 混合使用,提供从实体同步数据库,或者从数据库生成实体代码,支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite 数据库。</Description>
|
||||||
|
@ -66,13 +66,15 @@ namespace FreeSql.Internal.CommonProvider {
|
|||||||
public List<T> Query<T>(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
public List<T> Query<T>(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
|
||||||
var names = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
|
var names = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
|
||||||
var ret = new List<T>();
|
var ret = new List<T>();
|
||||||
|
var type = typeof(T);
|
||||||
|
var defaultValue = default(T);
|
||||||
ExecuteReader(dr => {
|
ExecuteReader(dr => {
|
||||||
if (names.Any() == false)
|
if (names.Any() == false)
|
||||||
for (var a = 0; a < dr.FieldCount; a++) names.Add(dr.GetName(a), a);
|
for (var a = 0; a < dr.FieldCount; a++) names.Add(dr.GetName(a), a);
|
||||||
object[] values = new object[dr.FieldCount];
|
object[] values = new object[names.Count];
|
||||||
dr.GetValues(values);
|
dr.GetValues(values);
|
||||||
var read = Utils.ExecuteArrayRowReadClassOrTuple(typeof(T), names, values, 0);
|
var read = Utils.ExecuteArrayRowReadClassOrTuple(type, names, values, 0);
|
||||||
ret.Add(read.value == null ? default(T) : (T)read.value);
|
ret.Add(read.Value == null ? defaultValue : (T)read.Value);
|
||||||
}, cmdType, cmdText, cmdParms);
|
}, cmdType, cmdText, cmdParms);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ namespace FreeSql.Internal.CommonProvider {
|
|||||||
var ret = new List<T>();
|
var ret = new List<T>();
|
||||||
foreach (var row in ds) {
|
foreach (var row in ds) {
|
||||||
var read = Utils.ExecuteArrayRowReadClassOrTuple(typeof(T), names, row);
|
var read = Utils.ExecuteArrayRowReadClassOrTuple(typeof(T), names, row);
|
||||||
ret.Add(read.value == null ? default(T) : (T) read.value);
|
ret.Add(read.Value == null ? default(T) : (T) read.Value);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ namespace FreeSql.Internal.CommonProvider {
|
|||||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql, _params.ToArray());
|
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql, _params.ToArray());
|
||||||
foreach (var dr in ds) {
|
foreach (var dr in ds) {
|
||||||
var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr);
|
var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr);
|
||||||
ret.Add(read.value == null ? default(TTuple) : (TTuple)read.value);
|
ret.Add(read.Value == null ? default(TTuple) : (TTuple)read.Value);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
@ -165,7 +165,7 @@ namespace FreeSql.Internal.CommonProvider {
|
|||||||
var ds = await _orm.Ado.ExecuteArrayAsync(CommandType.Text, sql, _params.ToArray());
|
var ds = await _orm.Ado.ExecuteArrayAsync(CommandType.Text, sql, _params.ToArray());
|
||||||
foreach (var dr in ds) {
|
foreach (var dr in ds) {
|
||||||
var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr);
|
var read = Utils.ExecuteArrayRowReadClassOrTuple(type, null, dr);
|
||||||
ret.Add(read.value == null ? default(TTuple) : (TTuple)read.value);
|
ret.Add(read.Value == null ? default(TTuple) : (TTuple)read.Value);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
@ -221,16 +221,12 @@ namespace FreeSql.Internal.CommonProvider {
|
|||||||
}
|
}
|
||||||
protected (ReadAnonymousTypeInfo map, string field) GetAllField() {
|
protected (ReadAnonymousTypeInfo map, string field) GetAllField() {
|
||||||
var type = typeof(T1);
|
var type = typeof(T1);
|
||||||
if (Utils._dicClassConstructor.TryGetValue(type, out var classInfo) == false) {
|
var map = new ReadAnonymousTypeInfo { Consturctor = type.GetConstructor(new Type[0]), ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Properties };
|
||||||
classInfo = new Utils._dicClassConstructorInfo { Constructor = type.GetConstructor(new Type[0]), Properties = type.GetProperties() };
|
|
||||||
Utils._dicClassConstructor.TryAdd(type, classInfo);
|
|
||||||
}
|
|
||||||
var map = new ReadAnonymousTypeInfo { Consturctor = classInfo.Constructor, ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Properties };
|
|
||||||
var field = new StringBuilder();
|
var field = new StringBuilder();
|
||||||
var dicfield = new Dictionary<string, bool>();
|
var dicfield = new Dictionary<string, bool>();
|
||||||
var tb = _tables.First();
|
var tb = _tables.First();
|
||||||
var index = 0;
|
var index = 0;
|
||||||
var ps = classInfo.Properties;
|
var ps = type.GetProperties();
|
||||||
foreach (var p in ps) {
|
foreach (var p in ps) {
|
||||||
var child = new ReadAnonymousTypeInfo { Property = p, CsName = p.Name };
|
var child = new ReadAnonymousTypeInfo { Property = p, CsName = p.Name };
|
||||||
if (tb.Table.ColumnsByCs.TryGetValue(p.Name, out var col)) { //普通字段
|
if (tb.Table.ColumnsByCs.TryGetValue(p.Name, out var col)) { //普通字段
|
||||||
|
463
FreeSql/Internal/UtilsExpressionTree.cs
Normal file
463
FreeSql/Internal/UtilsExpressionTree.cs
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
using FreeSql.DataAnnotations;
|
||||||
|
using FreeSql.Internal.Model;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Npgsql.LegacyPostgis;
|
||||||
|
using NpgsqlTypes;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace FreeSql.Internal {
|
||||||
|
class Utils {
|
||||||
|
|
||||||
|
static ConcurrentDictionary<string, TableInfo> _cacheGetTableByEntity = new ConcurrentDictionary<string, TableInfo>();
|
||||||
|
internal static TableInfo GetTableByEntity(Type entity, CommonUtils common) {
|
||||||
|
if (entity.FullName.StartsWith("<>f__AnonymousType")) return null;
|
||||||
|
if (_cacheGetTableByEntity.TryGetValue($"{common.DbName}-{entity.FullName}", out var trytb)) return trytb; //区分数据库类型缓存
|
||||||
|
if (common.CodeFirst.GetDbInfo(entity) != null) return null;
|
||||||
|
|
||||||
|
var tbattr = entity.GetCustomAttributes(typeof(TableAttribute), false).LastOrDefault() as TableAttribute;
|
||||||
|
trytb = new TableInfo();
|
||||||
|
trytb.Type = entity;
|
||||||
|
trytb.Properties = entity.GetProperties().ToDictionary(a => a.Name, a => a, StringComparer.CurrentCultureIgnoreCase);
|
||||||
|
trytb.CsName = entity.Name;
|
||||||
|
trytb.DbName = (tbattr?.Name ?? entity.Name);
|
||||||
|
trytb.DbOldName = tbattr?.OldName;
|
||||||
|
if (common.CodeFirst.IsSyncStructureToLower) {
|
||||||
|
trytb.DbName = trytb.DbName.ToLower();
|
||||||
|
trytb.DbOldName = trytb.DbOldName?.ToLower();
|
||||||
|
}
|
||||||
|
trytb.SelectFilter = tbattr?.SelectFilter;
|
||||||
|
foreach (var p in trytb.Properties.Values) {
|
||||||
|
var tp = common.CodeFirst.GetDbInfo(p.PropertyType);
|
||||||
|
//if (tp == null) continue;
|
||||||
|
var colattr = p.GetCustomAttributes(typeof(ColumnAttribute), false).LastOrDefault() as ColumnAttribute;
|
||||||
|
if (tp == null && colattr == null) continue;
|
||||||
|
if (colattr == null)
|
||||||
|
colattr = new ColumnAttribute {
|
||||||
|
Name = p.Name,
|
||||||
|
DbType = tp.Value.dbtypeFull,
|
||||||
|
IsIdentity = false,
|
||||||
|
IsNullable = tp.Value.isnullable ?? true,
|
||||||
|
IsPrimary = false,
|
||||||
|
};
|
||||||
|
if (string.IsNullOrEmpty(colattr.DbType)) colattr.DbType = tp?.dbtypeFull ?? "varchar(255)";
|
||||||
|
colattr.DbType = colattr.DbType.ToUpper();
|
||||||
|
|
||||||
|
if (tp != null && tp.Value.isnullable == null) colattr.IsNullable = tp.Value.dbtypeFull.Contains("NOT NULL") == false;
|
||||||
|
if (colattr.DbType?.Contains("NOT NULL") == true) colattr.IsNullable = false;
|
||||||
|
if (string.IsNullOrEmpty(colattr.Name)) colattr.Name = p.Name;
|
||||||
|
if (common.CodeFirst.IsSyncStructureToLower) colattr.Name = colattr.Name.ToLower();
|
||||||
|
|
||||||
|
if ((colattr.IsNullable == false || colattr.IsIdentity || colattr.IsPrimary) && colattr.DbType.Contains("NOT NULL") == false) {
|
||||||
|
colattr.IsNullable = false;
|
||||||
|
colattr.DbType += " NOT NULL";
|
||||||
|
}
|
||||||
|
if (colattr.IsNullable == true && colattr.DbType.Contains("NOT NULL")) colattr.DbType = colattr.DbType.Replace("NOT NULL", "");
|
||||||
|
colattr.DbType = Regex.Replace(colattr.DbType, @"\([^\)]+\)", m => {
|
||||||
|
var tmpLt = Regex.Replace(m.Groups[0].Value, @"\s", "");
|
||||||
|
if (tmpLt.Contains("CHAR")) tmpLt = tmpLt.Replace("CHAR", " CHAR");
|
||||||
|
if (tmpLt.Contains("BYTE")) tmpLt = tmpLt.Replace("BYTE", " BYTE");
|
||||||
|
return tmpLt;
|
||||||
|
});
|
||||||
|
colattr.DbDefautValue = trytb.Properties[p.Name].GetValue(Activator.CreateInstance(trytb.Type));
|
||||||
|
if (colattr.DbDefautValue == null) colattr.DbDefautValue = tp?.defaultValue;
|
||||||
|
if (colattr.IsNullable == false && colattr.DbDefautValue == null) {
|
||||||
|
var consturctorType = p.PropertyType.GenericTypeArguments.FirstOrDefault() ?? p.PropertyType;
|
||||||
|
colattr.DbDefautValue = Activator.CreateInstance(consturctorType);
|
||||||
|
}
|
||||||
|
|
||||||
|
var col = new ColumnInfo {
|
||||||
|
Table = trytb,
|
||||||
|
CsName = p.Name,
|
||||||
|
CsType = p.PropertyType,
|
||||||
|
Attribute = colattr
|
||||||
|
};
|
||||||
|
trytb.Columns.Add(colattr.Name, col);
|
||||||
|
trytb.ColumnsByCs.Add(p.Name, col);
|
||||||
|
}
|
||||||
|
trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsPrimary).ToArray();
|
||||||
|
if (trytb.Primarys.Any() == false) {
|
||||||
|
trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsIdentity).ToArray();
|
||||||
|
foreach(var col in trytb.Primarys)
|
||||||
|
col.Attribute.IsPrimary = true;
|
||||||
|
}
|
||||||
|
_cacheGetTableByEntity.TryAdd(entity.FullName, trytb);
|
||||||
|
return trytb;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T[] GetDbParamtersByObject<T>(string sql, object obj, string paramPrefix, Func<string, Type, object, T> constructorParamter) {
|
||||||
|
if (string.IsNullOrEmpty(sql) || obj == null) return new T[0];
|
||||||
|
var ttype = typeof(T);
|
||||||
|
var type = obj.GetType();
|
||||||
|
if (type == ttype) return new[] { (T)Convert.ChangeType(obj, type) };
|
||||||
|
var ret = new List<T>();
|
||||||
|
var ps = type.GetProperties();
|
||||||
|
foreach (var p in ps) {
|
||||||
|
if (sql.IndexOf($"{paramPrefix}{p.Name}", StringComparison.CurrentCultureIgnoreCase) == -1) continue;
|
||||||
|
var pvalue = p.GetValue(obj);
|
||||||
|
if (p.PropertyType == ttype) ret.Add((T)Convert.ChangeType(pvalue, ttype));
|
||||||
|
else ret.Add(constructorParamter(p.Name, p.PropertyType, pvalue));
|
||||||
|
}
|
||||||
|
return ret.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Dictionary<Type, bool> dicExecuteArrayRowReadClassOrTuple = new Dictionary<Type, bool> {
|
||||||
|
[typeof(bool)] = true,
|
||||||
|
[typeof(sbyte)] = true,
|
||||||
|
[typeof(short)] = true,
|
||||||
|
[typeof(int)] = true,
|
||||||
|
[typeof(long)] = true,
|
||||||
|
[typeof(byte)] = true,
|
||||||
|
[typeof(ushort)] = true,
|
||||||
|
[typeof(uint)] = true,
|
||||||
|
[typeof(ulong)] = true,
|
||||||
|
[typeof(double)] = true,
|
||||||
|
[typeof(float)] = true,
|
||||||
|
[typeof(decimal)] = true,
|
||||||
|
[typeof(TimeSpan)] = true,
|
||||||
|
[typeof(DateTime)] = true,
|
||||||
|
[typeof(DateTimeOffset)] = true,
|
||||||
|
[typeof(byte[])] = true,
|
||||||
|
[typeof(string)] = true,
|
||||||
|
[typeof(Guid)] = true,
|
||||||
|
[typeof(MygisPoint)] = true,
|
||||||
|
[typeof(MygisLineString)] = true,
|
||||||
|
[typeof(MygisPolygon)] = true,
|
||||||
|
[typeof(MygisMultiPoint)] = true,
|
||||||
|
[typeof(MygisMultiLineString)] = true,
|
||||||
|
[typeof(MygisMultiPolygon)] = true,
|
||||||
|
[typeof(BitArray)] = true,
|
||||||
|
[typeof(NpgsqlPoint)] = true,
|
||||||
|
[typeof(NpgsqlLine)] = true,
|
||||||
|
[typeof(NpgsqlLSeg)] = true,
|
||||||
|
[typeof(NpgsqlBox)] = true,
|
||||||
|
[typeof(NpgsqlPath)] = true,
|
||||||
|
[typeof(NpgsqlPolygon)] = true,
|
||||||
|
[typeof(NpgsqlCircle)] = true,
|
||||||
|
[typeof((IPAddress Address, int Subnet))] = true,
|
||||||
|
[typeof(IPAddress)] = true,
|
||||||
|
[typeof(PhysicalAddress)] = true,
|
||||||
|
[typeof(NpgsqlRange<int>)] = true,
|
||||||
|
[typeof(NpgsqlRange<long>)] = true,
|
||||||
|
[typeof(NpgsqlRange<decimal>)] = true,
|
||||||
|
[typeof(NpgsqlRange<DateTime>)] = true,
|
||||||
|
[typeof(PostgisPoint)] = true,
|
||||||
|
[typeof(PostgisLineString)] = true,
|
||||||
|
[typeof(PostgisPolygon)] = true,
|
||||||
|
[typeof(PostgisMultiPoint)] = true,
|
||||||
|
[typeof(PostgisMultiLineString)] = true,
|
||||||
|
[typeof(PostgisMultiPolygon)] = true,
|
||||||
|
[typeof(PostgisGeometry)] = true,
|
||||||
|
[typeof(PostgisGeometryCollection)] = true,
|
||||||
|
[typeof(Dictionary<string, string>)] = true,
|
||||||
|
[typeof(JToken)] = true,
|
||||||
|
[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>>();
|
||||||
|
internal class RowInfo {
|
||||||
|
public object Value { get; set; }
|
||||||
|
public int DataIndex { get; set; }
|
||||||
|
public RowInfo(object value, int dataIndex) {
|
||||||
|
this.Value = value;
|
||||||
|
this.DataIndex = dataIndex;
|
||||||
|
}
|
||||||
|
public static ConstructorInfo Constructor = typeof(RowInfo).GetConstructor(new[] { typeof(object), typeof(int) });
|
||||||
|
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) {
|
||||||
|
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 dataIndexExp = Expression.Parameter(typeof(int), "dataIndex");
|
||||||
|
|
||||||
|
if (type.IsArray) return Expression.Lambda<Func<Type, Dictionary<string, int>, object[], int, RowInfo>>(
|
||||||
|
Expression.New(RowInfo.Constructor,
|
||||||
|
Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.ArrayAccess(rowExp, dataIndexExp) }),
|
||||||
|
Expression.Add(dataIndexExp, Expression.Constant(1))
|
||||||
|
), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile();
|
||||||
|
|
||||||
|
var typeGeneric = type;
|
||||||
|
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>>(
|
||||||
|
Expression.New(RowInfo.Constructor,
|
||||||
|
Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.ArrayAccess(rowExp, dataIndexExp) }),
|
||||||
|
Expression.Add(dataIndexExp, Expression.Constant(1))
|
||||||
|
), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile();
|
||||||
|
|
||||||
|
if (type.Namespace == "System" && (type.FullName == "System.String" || type.IsValueType)) { //值类型,或者元组
|
||||||
|
bool isTuple = type.Name.StartsWith("ValueTuple`");
|
||||||
|
if (isTuple) {
|
||||||
|
var ret2Exp = Expression.Variable(type, "ret");
|
||||||
|
var read2Exp = Expression.Variable(typeof(RowInfo), "read");
|
||||||
|
var read2ExpValue = Expression.MakeMemberAccess(read2Exp, RowInfo.PropertyValue);
|
||||||
|
var read2ExpDataIndex = Expression.MakeMemberAccess(read2Exp, RowInfo.PropertyDataIndex);
|
||||||
|
var block2Exp = new List<Expression>();
|
||||||
|
|
||||||
|
var fields = type.GetFields();
|
||||||
|
foreach (var field in fields) {
|
||||||
|
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.IfThen(Expression.GreaterThan(read2ExpDataIndex, dataIndexExp),
|
||||||
|
Expression.Assign(dataIndexExp, read2ExpDataIndex)),
|
||||||
|
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)))),
|
||||||
|
// Expression.IfThen(Expression.Equal(read2ExpDataIndex, Expression.Constant(1)), Expression.Throw(Expression.Constant(new Exception(field.Name + "," + 1)))),
|
||||||
|
// Expression.IfThen(Expression.Equal(read2ExpDataIndex, Expression.Constant(2)), Expression.Throw(Expression.Constant(new Exception(field.Name + "," + 2)))),
|
||||||
|
// Expression.IfThen(Expression.Equal(read2ExpDataIndex, Expression.Constant(3)), Expression.Throw(Expression.Constant(new Exception(field.Name + "," + 3)))),
|
||||||
|
// Expression.IfThen(Expression.Equal(read2ExpDataIndex, Expression.Constant(4)), Expression.Throw(Expression.Constant(new Exception(field.Name + "," + 4))))
|
||||||
|
// )
|
||||||
|
//))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
block2Exp.AddRange(new Expression[] {
|
||||||
|
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>>(
|
||||||
|
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>>(
|
||||||
|
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.Add(dataIndexExp, Expression.Constant(1))))
|
||||||
|
),
|
||||||
|
Expression.Label(returnTarget, Expression.Default(typeof(RowInfo)))
|
||||||
|
), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == typeof(object) && names != null) {
|
||||||
|
Func<Type, Dictionary<string, int>, object[], 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]);
|
||||||
|
return new RowInfo(expando, names2.Count);
|
||||||
|
};
|
||||||
|
return dynamicFunc;// Expression.Lambda<Func<Type, Dictionary<string, int>, object[], int, RowInfo>>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
//类注入属性
|
||||||
|
var retExp = Expression.Variable(type, "ret");
|
||||||
|
var readExp = Expression.Variable(typeof(RowInfo), "read");
|
||||||
|
var readExpValue = Expression.MakeMemberAccess(readExp, RowInfo.PropertyValue);
|
||||||
|
var readExpDataIndex = Expression.MakeMemberAccess(readExp, RowInfo.PropertyDataIndex);
|
||||||
|
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)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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 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);
|
||||||
|
|
||||||
|
static ConcurrentDictionary<string, Action<object, object>> _dicFillPropertyValue = new ConcurrentDictionary<string, Action<object, object>>();
|
||||||
|
internal static void FillPropertyValue(object info, string memberAccessPath, object value) {
|
||||||
|
var typeObj = info.GetType();
|
||||||
|
var typeValue = value.GetType();
|
||||||
|
var key = "FillPropertyValue_" + typeObj.FullName + "_" + typeValue.FullName;
|
||||||
|
var act = _dicFillPropertyValue.GetOrAdd($"{key}.{memberAccessPath}", s => {
|
||||||
|
var parmInfo = Expression.Parameter(typeof(object), "info");
|
||||||
|
var parmValue = Expression.Parameter(typeof(object), "value");
|
||||||
|
Expression exp = Expression.Convert(parmInfo, typeObj);
|
||||||
|
foreach (var pro in memberAccessPath.Split('.'))
|
||||||
|
exp = Expression.PropertyOrField(exp, pro) ?? throw new Exception(string.Concat(exp.Type.FullName, " 没有定义属性 ", pro));
|
||||||
|
|
||||||
|
var value2 = Expression.Call(MethodGetDataReaderValue, Expression.Constant(exp.Type), parmValue);
|
||||||
|
var value3 = Expression.Convert(parmValue, typeValue);
|
||||||
|
exp = Expression.Assign(exp, value3);
|
||||||
|
return Expression.Lambda<Action<object, object>>(exp, parmInfo, parmValue).Compile();
|
||||||
|
});
|
||||||
|
act(info, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConcurrentDictionary<Type, Func<object, object>> _dicGetDataReaderValue = new ConcurrentDictionary<Type, Func<object, object>>();
|
||||||
|
internal static object GetDataReaderValue(Type type, object value) {
|
||||||
|
if (value == null || value == DBNull.Value) return null;
|
||||||
|
|
||||||
|
var func = _dicGetDataReaderValue.GetOrAdd(type, k => {
|
||||||
|
var returnTarget = Expression.Label(typeof(object));
|
||||||
|
var parmExp = Expression.Parameter(typeof(object), "value");
|
||||||
|
|
||||||
|
if (type.FullName == "System.Byte[]") return Expression.Lambda<Func<object, object>>(parmExp, parmExp).Compile();
|
||||||
|
|
||||||
|
if (type.IsArray) {
|
||||||
|
var elementType = type.GetElementType();
|
||||||
|
var valueArr = value as Array;
|
||||||
|
|
||||||
|
if (elementType == valueArr.GetType().GetElementType()) return Expression.Lambda<Func<object, object>>(parmExp, parmExp).Compile();
|
||||||
|
|
||||||
|
var arr = Expression.Variable(type, "arr");
|
||||||
|
var arrlen = Expression.Variable(typeof(int), "arrlen");
|
||||||
|
var x = Expression.Variable(typeof(int), "x");
|
||||||
|
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)),
|
||||||
|
Expression.Assign(arrlen, Expression.ArrayLength(arr)),
|
||||||
|
Expression.Assign(x, Expression.Constant(0)),
|
||||||
|
ret,
|
||||||
|
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.PostIncrementAssign(x)
|
||||||
|
),
|
||||||
|
Expression.Break(label, x)
|
||||||
|
),
|
||||||
|
label
|
||||||
|
)
|
||||||
|
), 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) }),
|
||||||
|
Expression.Constant(type, typeof(Type)),
|
||||||
|
Expression.Convert(parmExp, typeof(string)),
|
||||||
|
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();
|
||||||
|
|
||||||
|
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))),
|
||||||
|
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))),
|
||||||
|
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))),
|
||||||
|
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))),
|
||||||
|
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))),
|
||||||
|
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))),
|
||||||
|
typeof(MygisMultiPolygon)
|
||||||
|
), parmExp).Compile();
|
||||||
|
case "Newtonsoft.Json.Linq.JToken": return Expression.Lambda<Func<object, object>>(
|
||||||
|
Expression.TypeAs(
|
||||||
|
Expression.Call(typeof(JToken).GetMethod("Parse", new[] { typeof(string) }), Expression.Convert(parmExp, typeof(string))),
|
||||||
|
typeof(JToken)
|
||||||
|
), parmExp).Compile();
|
||||||
|
case "Newtonsoft.Json.Linq.JObject": return Expression.Lambda<Func<object, object>>(
|
||||||
|
Expression.TypeAs(
|
||||||
|
Expression.Call(typeof(JObject).GetMethod("Parse", new[] { typeof(string) }), Expression.Convert(parmExp, typeof(string))),
|
||||||
|
typeof(JObject)
|
||||||
|
), parmExp).Compile();
|
||||||
|
case "Newtonsoft.Json.Linq.JArray": return Expression.Lambda<Func<object, object>>(
|
||||||
|
Expression.TypeAs(
|
||||||
|
Expression.Call(typeof(JArray).GetMethod("Parse", new[] { typeof(string) }), Expression.Convert(parmExp, typeof(string))),
|
||||||
|
typeof(JArray)
|
||||||
|
), parmExp).Compile();
|
||||||
|
case "Npgsql.LegacyPostgis.PostgisGeometry": return Expression.Lambda<Func<object, object>>(parmExp, parmExp).Compile();
|
||||||
|
}
|
||||||
|
if (type != value.GetType()) {
|
||||||
|
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();
|
||||||
|
return Expression.Lambda<Func<object, object>>(
|
||||||
|
Expression.Call(typeof(Convert).GetMethod("ChangeType", new[] { typeof(object), typeof(Type) }), parmExp, Expression.Constant(type, typeof(Type)))
|
||||||
|
, parmExp).Compile();
|
||||||
|
}
|
||||||
|
return Expression.Lambda<Func<object, object>>(parmExp, parmExp).Compile();
|
||||||
|
});
|
||||||
|
return func(value);
|
||||||
|
}
|
||||||
|
internal static string GetCsName(string name) {
|
||||||
|
name = Regex.Replace(name.TrimStart('@'), @"[^\w]", "_");
|
||||||
|
return char.IsLetter(name, 0) ? name : string.Concat("_", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,7 @@ using System.Text.RegularExpressions;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace FreeSql.Internal {
|
namespace FreeSql.Internal {
|
||||||
class Utils {
|
class UtilsReflection {
|
||||||
|
|
||||||
static ConcurrentDictionary<string, TableInfo> _cacheGetTableByEntity = new ConcurrentDictionary<string, TableInfo>();
|
static ConcurrentDictionary<string, TableInfo> _cacheGetTableByEntity = new ConcurrentDictionary<string, TableInfo>();
|
||||||
internal static TableInfo GetTableByEntity(Type entity, CommonUtils common) {
|
internal static TableInfo GetTableByEntity(Type entity, CommonUtils common) {
|
||||||
@ -110,59 +110,60 @@ namespace FreeSql.Internal {
|
|||||||
return ret.ToArray();
|
return ret.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Dictionary<string, bool> dicExecuteArrayRowReadClassOrTuple = new Dictionary<string, bool> {
|
static Dictionary<Type, bool> dicExecuteArrayRowReadClassOrTuple = new Dictionary<Type, bool> {
|
||||||
{ typeof(bool).FullName, true },
|
[typeof(bool)] = true,
|
||||||
{ typeof(sbyte).FullName, true },
|
[typeof(sbyte)] = true,
|
||||||
{ typeof(short).FullName, true },
|
[typeof(short)] = true,
|
||||||
{ typeof(int).FullName, true },
|
[typeof(int)] = true,
|
||||||
{ typeof(long).FullName, true },
|
[typeof(long)] = true,
|
||||||
{ typeof(byte).FullName, true },
|
[typeof(byte)] = true,
|
||||||
{ typeof(ushort).FullName, true },
|
[typeof(ushort)] = true,
|
||||||
{ typeof(uint).FullName, true },
|
[typeof(uint)] = true,
|
||||||
{ typeof(ulong).FullName, true },
|
[typeof(ulong)] = true,
|
||||||
{ typeof(double).FullName, true },
|
[typeof(double)] = true,
|
||||||
{ typeof(float).FullName, true },
|
[typeof(float)] = true,
|
||||||
{ typeof(decimal).FullName, true },
|
[typeof(decimal)] = true,
|
||||||
{ typeof(TimeSpan).FullName, true },
|
[typeof(TimeSpan)] = true,
|
||||||
{ typeof(DateTime).FullName, true },
|
[typeof(DateTime)] = true,
|
||||||
{ typeof(DateTimeOffset).FullName, true },
|
[typeof(DateTimeOffset)] = true,
|
||||||
{ typeof(byte[]).FullName, true },
|
[typeof(byte[])] = true,
|
||||||
{ typeof(string).FullName, true },
|
[typeof(string)] = true,
|
||||||
{ typeof(Guid).FullName, true },
|
[typeof(Guid)] = true,
|
||||||
{ typeof(MygisPoint).FullName, true },
|
[typeof(MygisPoint)] = true,
|
||||||
{ typeof(MygisLineString).FullName, true },
|
[typeof(MygisLineString)] = true,
|
||||||
{ typeof(MygisPolygon).FullName, true },
|
[typeof(MygisPolygon)] = true,
|
||||||
{ typeof(MygisMultiPoint).FullName, true },
|
[typeof(MygisMultiPoint)] = true,
|
||||||
{ typeof(MygisMultiLineString).FullName, true },
|
[typeof(MygisMultiLineString)] = true,
|
||||||
{ typeof(MygisMultiPolygon).FullName, true },
|
[typeof(MygisMultiPolygon)] = true,
|
||||||
{ typeof(BitArray).FullName, true },
|
[typeof(BitArray)] = true,
|
||||||
{ typeof(NpgsqlPoint).FullName, true },
|
[typeof(NpgsqlPoint)] = true,
|
||||||
{ typeof(NpgsqlLine).FullName, true },
|
[typeof(NpgsqlLine)] = true,
|
||||||
{ typeof(NpgsqlLSeg).FullName, true },
|
[typeof(NpgsqlLSeg)] = true,
|
||||||
{ typeof(NpgsqlBox).FullName, true },
|
[typeof(NpgsqlBox)] = true,
|
||||||
{ typeof(NpgsqlPath).FullName, true },
|
[typeof(NpgsqlPath)] = true,
|
||||||
{ typeof(NpgsqlPolygon).FullName, true },
|
[typeof(NpgsqlPolygon)] = true,
|
||||||
{ typeof(NpgsqlCircle).FullName, true },
|
[typeof(NpgsqlCircle)] = true,
|
||||||
{ typeof((IPAddress Address, int Subnet)).FullName, true },
|
[typeof((IPAddress Address, int Subnet))] = true,
|
||||||
{ typeof(IPAddress).FullName, true },
|
[typeof(IPAddress)] = true,
|
||||||
{ typeof(PhysicalAddress).FullName, true },
|
[typeof(PhysicalAddress)] = true,
|
||||||
{ typeof(NpgsqlRange<int>).FullName, true },
|
[typeof(NpgsqlRange<int>)] = true,
|
||||||
{ typeof(NpgsqlRange<long>).FullName, true },
|
[typeof(NpgsqlRange<long>)] = true,
|
||||||
{ typeof(NpgsqlRange<decimal>).FullName, true },
|
[typeof(NpgsqlRange<decimal>)] = true,
|
||||||
{ typeof(NpgsqlRange<DateTime>).FullName, true },
|
[typeof(NpgsqlRange<DateTime>)] = true,
|
||||||
{ typeof(PostgisPoint).FullName, true },
|
[typeof(PostgisPoint)] = true,
|
||||||
{ typeof(PostgisLineString).FullName, true },
|
[typeof(PostgisLineString)] = true,
|
||||||
{ typeof(PostgisPolygon).FullName, true },
|
[typeof(PostgisPolygon)] = true,
|
||||||
{ typeof(PostgisMultiPoint).FullName, true },
|
[typeof(PostgisMultiPoint)] = true,
|
||||||
{ typeof(PostgisMultiLineString).FullName, true },
|
[typeof(PostgisMultiLineString)] = true,
|
||||||
{ typeof(PostgisMultiPolygon).FullName, true },
|
[typeof(PostgisMultiPolygon)] = true,
|
||||||
{ typeof(PostgisGeometry).FullName, true },
|
[typeof(PostgisGeometry)] = true,
|
||||||
{ typeof(PostgisGeometryCollection).FullName, true },
|
[typeof(PostgisGeometryCollection)] = true,
|
||||||
{ typeof(Dictionary<string, string>).FullName, true },
|
[typeof(Dictionary<string, string>)] = true,
|
||||||
{ typeof(JToken).FullName, true },
|
[typeof(JToken)] = true,
|
||||||
{ typeof(JObject).FullName, true },
|
[typeof(JObject)] = true,
|
||||||
{ typeof(JArray).FullName, true },
|
[typeof(JArray)] = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static ConcurrentDictionary<Type, _dicClassConstructorInfo> _dicClassConstructor = new ConcurrentDictionary<Type, _dicClassConstructorInfo>();
|
internal static ConcurrentDictionary<Type, _dicClassConstructorInfo> _dicClassConstructor = new ConcurrentDictionary<Type, _dicClassConstructorInfo>();
|
||||||
internal static ConcurrentDictionary<Type, _dicTupleConstructorInfo> _dicTupleConstructor = new ConcurrentDictionary<Type, _dicTupleConstructorInfo>();
|
internal static ConcurrentDictionary<Type, _dicTupleConstructorInfo> _dicTupleConstructor = new ConcurrentDictionary<Type, _dicTupleConstructorInfo>();
|
||||||
internal class _dicClassConstructorInfo {
|
internal class _dicClassConstructorInfo {
|
||||||
@ -178,7 +179,7 @@ namespace FreeSql.Internal {
|
|||||||
var typeGeneric = type;
|
var typeGeneric = type;
|
||||||
if (typeGeneric.FullName.StartsWith("System.Nullable`1[")) typeGeneric = type.GenericTypeArguments.First();
|
if (typeGeneric.FullName.StartsWith("System.Nullable`1[")) typeGeneric = type.GenericTypeArguments.First();
|
||||||
if (typeGeneric.IsEnum ||
|
if (typeGeneric.IsEnum ||
|
||||||
dicExecuteArrayRowReadClassOrTuple.ContainsKey(typeGeneric.FullName))
|
dicExecuteArrayRowReadClassOrTuple.ContainsKey(typeGeneric))
|
||||||
return (GetDataReaderValue(type, row[dataIndex]), dataIndex + 1);
|
return (GetDataReaderValue(type, row[dataIndex]), dataIndex + 1);
|
||||||
if (type.Namespace == "System" && (type.FullName == "System.String" || type.IsValueType)) { //值类型,或者元组
|
if (type.Namespace == "System" && (type.FullName == "System.String" || type.IsValueType)) { //值类型,或者元组
|
||||||
bool isTuple = type.Name.StartsWith("ValueTuple`");
|
bool isTuple = type.Name.StartsWith("ValueTuple`");
|
||||||
@ -216,7 +217,7 @@ namespace FreeSql.Internal {
|
|||||||
if (names != null && names.TryGetValue(prop.Name, out tryidx) == false) continue;
|
if (names != null && names.TryGetValue(prop.Name, out tryidx) == false) continue;
|
||||||
var read = ExecuteArrayRowReadClassOrTuple(prop.PropertyType, names, row, tryidx);
|
var read = ExecuteArrayRowReadClassOrTuple(prop.PropertyType, names, row, tryidx);
|
||||||
if (read.dataIndex > dataIndex) dataIndex = read.dataIndex;
|
if (read.dataIndex > dataIndex) dataIndex = read.dataIndex;
|
||||||
prop.SetValue(value, GetDataReaderValue(prop.PropertyType, read.value), null);
|
prop.SetValue(value, read.value, null);
|
||||||
//FillPropertyValue(value, p.Name, read.value);
|
//FillPropertyValue(value, p.Name, read.value);
|
||||||
//p.SetValue(value, read.value);
|
//p.SetValue(value, read.value);
|
||||||
}
|
}
|
27
readme.md
27
readme.md
@ -160,30 +160,29 @@ List<dynamic> t8 = fsql.Ado.Query<dynamic>("select * from song");
|
|||||||
|
|
||||||
## 性能测试
|
## 性能测试
|
||||||
|
|
||||||
### FreeSql ToList & Dapper Query
|
|
||||||
|
|
||||||
Elapsed: 00:00:00.9666720; Query Entity Counts: 131072; ORM: Dapper
|
|
||||||
|
|
||||||
Elapsed: 00:00:01.4215325; ToList Entity Counts: 131072; ORM: FreeSql*
|
|
||||||
|
|
||||||
### FreeSql Query & Dapper Query
|
### FreeSql Query & Dapper Query
|
||||||
|
|
||||||
Elapsed: 00:00:00.9728656; Query Entity Counts: 131072; ORM: Dapper
|
Elapsed: 00:00:01.6999868; Query Entity Counts: 131072; ORM: Dapper
|
||||||
|
|
||||||
Elapsed: 00:00:00.4484073; Query Tuple Counts: 131072; ORM: Dapper
|
Elapsed: 00:00:00.4200430; Query Tuple Counts: 131072; ORM: Dapper
|
||||||
|
|
||||||
Elapsed: 00:00:00.6580620; Query Dynamic Counts: 131072; ORM: Dapper
|
Elapsed: 00:00:00.6615716; Query Dynamic Counts: 131072; ORM: Dapper
|
||||||
|
|
||||||
Elapsed: 00:00:02.6804199; Query Entity Counts: 131072; ORM: FreeSql*
|
Elapsed: 00:00:02.4955996; Query Entity Counts: 131072; ORM: FreeSql*
|
||||||
|
|
||||||
Elapsed: 00:00:01.4161527; Query Tuple Counts: 131072; ORM: FreeSql*
|
Elapsed: 00:00:01.2938320; Query Tuple Counts: 131072; ORM: FreeSql*
|
||||||
|
|
||||||
Elapsed: 00:00:00.9965082; Query Dynamic Counts: 131072; ORM: FreeSql*
|
Elapsed: 00:00:00.9719682; Query Dynamic Counts: 131072; ORM: FreeSql*
|
||||||
|
|
||||||
|
### FreeSql ToList & Dapper Query
|
||||||
|
|
||||||
|
Elapsed: 00:00:01.7446031; Query Entity Counts: 131072; ORM: Dapper
|
||||||
|
|
||||||
|
Elapsed: 00:00:01.2857691; ToList Entity Counts: 131072; ORM: FreeSql*
|
||||||
|
|
||||||
[查看测试代码](FreeSql.Tests.PerformanceTests/MySqlAdoTest.cs)
|
[查看测试代码](FreeSql.Tests.PerformanceTests/MySqlAdoTest.cs)
|
||||||
|
|
||||||
FreeSql 目前使用的反射+缓存,比不过 Dapper Emit 性能。
|
FreeSql 目前使用的反射+缓存,硬碰硬比不过 Dapper Emit,真实项目使用相差无几。Emit 在单次查询记录数量越多收益越大。
|
||||||
|
|
||||||
|
|
||||||
# Part2 添加
|
# Part2 添加
|
||||||
```csharp
|
```csharp
|
||||||
|
Loading…
x
Reference in New Issue
Block a user