反射+缓存性能优化,接近dapper

This commit is contained in:
28810 2019-01-15 18:36:43 +08:00
parent 57088da71b
commit ed239835c6
13 changed files with 98 additions and 23 deletions

View File

@ -6,6 +6,8 @@ using Xunit;
using Dapper; using Dapper;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
namespace FreeSql.Tests.PerformanceTest { namespace FreeSql.Tests.PerformanceTest {
public class MySqlAdoTest { public class MySqlAdoTest {
@ -143,6 +145,41 @@ namespace FreeSql.Tests.PerformanceTest {
sb.AppendLine($"Elapsed: {time.Elapsed}; ToList Entity Counts: {t3.Count}; ORM: FreeSql*"); sb.AppendLine($"Elapsed: {time.Elapsed}; ToList Entity Counts: {t3.Count}; ORM: FreeSql*");
} }
[Fact]
public void ToListLimit10() {
var sb = new StringBuilder();
var time = new Stopwatch();
time.Restart();
var dplist1Count = 0;
var p1 = Parallel.For(1, 50, b => {
List<xxx> dplist1 = new List<xxx>();
for (var a = 0; a < 1000; a++) {
using (var conn = g.mysql.Ado.MasterPool.Get()) {
dplist1.AddRange(Dapper.SqlMapper.Query<xxx>(conn.Value, "select * from song limit 50").ToList());
}
}
Interlocked.Exchange(ref dplist1Count, dplist1.Count);
});
while (p1.IsCompleted == false) ;
time.Stop();
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")]
class xxx { class xxx {
public int Id { get; set; } public int Id { get; set; }

View File

@ -7,7 +7,7 @@ using System.Text;
public class g { public class g {
public static IFreeSql mysql = new FreeSql.FreeSqlBuilder() public static IFreeSql mysql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=10") .UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=100")
.UseLogger(new LoggerFactory().CreateLogger("FreeSql.MySql")) .UseLogger(new LoggerFactory().CreateLogger("FreeSql.MySql"))
.UseAutoSyncStructure(false) .UseAutoSyncStructure(false)
.Build(); .Build();

View File

@ -34,6 +34,9 @@ namespace FreeSql.Tests.MySql {
[Fact] [Fact]
public void ToList() { public void ToList() {
var t0 = select.Limit(50).ToList();
var t1 = g.mysql.Select<TestInfo>().Where("").Where(a => a.Id > 0).Skip(100).Limit(200).ToSql(); var t1 = g.mysql.Select<TestInfo>().Where("").Where(a => a.Id > 0).Skip(100).Limit(200).ToSql();
var t2 = g.mysql.Select<TestInfo>().As("b").Where("").Where(a => a.Id > 0).Skip(100).Limit(200).ToSql(); var t2 = g.mysql.Select<TestInfo>().As("b").Where("").Where(a => a.Id > 0).Skip(100).Limit(200).ToSql();

View File

@ -58,7 +58,9 @@ namespace FreeSql.Internal {
parent.Consturctor = map.First().Table.Table.Type.GetConstructor(new Type[0]); parent.Consturctor = map.First().Table.Table.Type.GetConstructor(new Type[0]);
parent.ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Properties; parent.ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Properties;
for (var idx = 0; idx < map.Count; idx++) { for (var idx = 0; idx < map.Count; idx++) {
var child = new ReadAnonymousTypeInfo { CsName = map[idx].Column.CsName, DbField = $"{map[idx].Table.Alias}.{_common.QuoteSqlName(map[idx].Column.Attribute.Name)}" }; var child = new ReadAnonymousTypeInfo {
Property = map.First().Table.Table.Type.GetProperty(map[idx].Column.CsName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance),
CsName = map[idx].Column.CsName, DbField = $"{map[idx].Table.Alias}.{_common.QuoteSqlName(map[idx].Column.Attribute.Name)}" };
field.Append(", ").Append(_common.QuoteReadColumn(map[idx].Column.CsType, child.DbField)); field.Append(", ").Append(_common.QuoteReadColumn(map[idx].Column.CsType, child.DbField));
if (index >= 0) field.Append(" as").Append(++index); if (index >= 0) field.Append(" as").Append(++index);
parent.Childs.Add(child); parent.Childs.Add(child);
@ -75,7 +77,9 @@ namespace FreeSql.Internal {
parent.Consturctor = newExp.Type.GetConstructors()[0]; parent.Consturctor = newExp.Type.GetConstructors()[0];
parent.ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Arguments; parent.ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Arguments;
for (var a = 0; a < newExp.Members.Count; a++) { for (var a = 0; a < newExp.Members.Count; a++) {
var child = new ReadAnonymousTypeInfo { CsName = newExp.Members[a].Name, CsType = newExp.Arguments[a].Type }; var child = new ReadAnonymousTypeInfo {
Property = newExp.Type.GetProperty(newExp.Members[a].Name, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance),
CsName = newExp.Members[a].Name, CsType = newExp.Arguments[a].Type };
parent.Childs.Add(child); parent.Childs.Add(child);
ReadAnonymousField(_tables, field, child, ref index, newExp.Arguments[a], getSelectGroupingMapString); ReadAnonymousField(_tables, field, child, ref index, newExp.Arguments[a], getSelectGroupingMapString);
} }
@ -98,7 +102,8 @@ namespace FreeSql.Internal {
case ReadAnonymousTypeInfoConsturctorType.Properties: case ReadAnonymousTypeInfoConsturctorType.Properties:
var ret = parent.Consturctor.Invoke(null); var ret = parent.Consturctor.Invoke(null);
for (var b = 0; b < parent.Childs.Count; b++) { for (var b = 0; b < parent.Childs.Count; b++) {
Utils.FillPropertyValue(ret, parent.Childs[b].CsName, ReadAnonymous(parent.Childs[b], dr, ref index)); var prop = parent.Childs[b].Property;
prop.SetValue(ret, Utils.GetDataReaderValue(prop.PropertyType, ReadAnonymous(parent.Childs[b], dr, ref index)), null);
} }
return ret; return ret;
} }

View File

@ -1,5 +1,6 @@
using FreeSql.Internal.Model; using FreeSql.Internal.Model;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
@ -220,14 +221,18 @@ namespace FreeSql.Internal.CommonProvider {
} }
protected (ReadAnonymousTypeInfo map, string field) GetAllField() { protected (ReadAnonymousTypeInfo map, string field) GetAllField() {
var type = typeof(T1); var type = typeof(T1);
var map = new ReadAnonymousTypeInfo { Consturctor = type.GetConstructor(new Type[0]), ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Properties }; if (Utils._dicClassConstructor.TryGetValue(type, out var classInfo) == false) {
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 = typeof(T1).GetProperties(); var ps = classInfo.Properties;
foreach (var p in ps) { foreach (var p in ps) {
var child = new ReadAnonymousTypeInfo { 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)) { //普通字段
if (index > 0) field.Append(", "); if (index > 0) field.Append(", ");
var quoteName = _commonUtils.QuoteSqlName(col.Attribute.Name); var quoteName = _commonUtils.QuoteSqlName(col.Attribute.Name);
@ -248,7 +253,9 @@ namespace FreeSql.Internal.CommonProvider {
++index; ++index;
if (dicfield.ContainsKey(quoteName)) field.Append(" as").Append(index); if (dicfield.ContainsKey(quoteName)) field.Append(" as").Append(index);
else dicfield.Add(quoteName, true); else dicfield.Add(quoteName, true);
child.Childs.Add(new ReadAnonymousTypeInfo { CsName = col2.CsName }); child.Childs.Add(new ReadAnonymousTypeInfo {
Property = tb2.Table.Type.GetProperty(col2.CsName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance),
CsName = col2.CsName });
} }
} }
map.Childs.Add(child); map.Childs.Add(child);

View File

@ -20,6 +20,7 @@ namespace FreeSql.Internal {
internal abstract string Mod(string left, string right, Type leftType, Type rightType); internal abstract string Mod(string left, string right, Type leftType, Type rightType);
internal abstract string QuoteWriteParamter(Type type, string paramterName); internal abstract string QuoteWriteParamter(Type type, string paramterName);
internal abstract string QuoteReadColumn(Type type, string columnName); internal abstract string QuoteReadColumn(Type type, string columnName);
internal abstract string DbName { get; }
internal ICodeFirst CodeFirst { get; set; } internal ICodeFirst CodeFirst { get; set; }
internal TableInfo GetTableByEntity(Type entity) => Utils.GetTableByEntity(entity, this); internal TableInfo GetTableByEntity(Type entity) => Utils.GetTableByEntity(entity, this);

View File

@ -5,6 +5,7 @@ using System.Text;
namespace FreeSql.Internal.Model { namespace FreeSql.Internal.Model {
class ReadAnonymousTypeInfo { class ReadAnonymousTypeInfo {
public PropertyInfo Property { get; set; }
public string CsName { get; set; } public string CsName { get; set; }
public Type CsType { get; set; } public Type CsType { get; set; }
public string DbField { get; set; } public string DbField { get; set; }

View File

@ -21,7 +21,7 @@ namespace FreeSql.Internal {
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) {
if (entity.FullName.StartsWith("<>f__AnonymousType")) return null; if (entity.FullName.StartsWith("<>f__AnonymousType")) return null;
if (_cacheGetTableByEntity.TryGetValue($"{common.QuoteSqlName("db")}{entity.FullName}", out var trytb)) return trytb; //区分数据库类型缓存 if (_cacheGetTableByEntity.TryGetValue($"{common.DbName}-{entity.FullName}", out var trytb)) return trytb; //区分数据库类型缓存
if (common.CodeFirst.GetDbInfo(entity) != null) return null; if (common.CodeFirst.GetDbInfo(entity) != null) return null;
var tbattr = entity.GetCustomAttributes(typeof(TableAttribute), false).LastOrDefault() as TableAttribute; var tbattr = entity.GetCustomAttributes(typeof(TableAttribute), false).LastOrDefault() as TableAttribute;
@ -163,6 +163,16 @@ namespace FreeSql.Internal {
{ typeof(JObject).FullName, true }, { typeof(JObject).FullName, true },
{ typeof(JArray).FullName, true }, { typeof(JArray).FullName, true },
}; };
internal static ConcurrentDictionary<Type, _dicClassConstructorInfo> _dicClassConstructor = new ConcurrentDictionary<Type, _dicClassConstructorInfo>();
internal static ConcurrentDictionary<Type, _dicTupleConstructorInfo> _dicTupleConstructor = new ConcurrentDictionary<Type, _dicTupleConstructorInfo>();
internal class _dicClassConstructorInfo {
public ConstructorInfo Constructor { get; set; }
public PropertyInfo[] Properties { get; set; }
}
internal class _dicTupleConstructorInfo {
public ConstructorInfo Constructor { get; set; }
public Type[] Types { get; set; }
}
internal static (object value, int dataIndex) ExecuteArrayRowReadClassOrTuple(Type type, Dictionary<string, int> names, object[] row, int dataIndex = 0) { internal static (object value, int dataIndex) ExecuteArrayRowReadClassOrTuple(Type type, Dictionary<string, int> names, object[] row, int dataIndex = 0) {
if (type.IsArray) return (GetDataReaderValue(type, row[dataIndex]), dataIndex + 1); if (type.IsArray) return (GetDataReaderValue(type, row[dataIndex]), dataIndex + 1);
var typeGeneric = type; var typeGeneric = type;
@ -173,17 +183,18 @@ namespace FreeSql.Internal {
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`");
if (isTuple) { if (isTuple) {
var fs = type.GetFields(); if (_dicTupleConstructor.TryGetValue(type, out var tupleInfo) == false) {
var types = new Type[fs.Length]; var types = type.GetFields().Select(a => a.FieldType).ToArray();
var parms = new object[fs.Length]; tupleInfo = new _dicTupleConstructorInfo { Constructor = type.GetConstructor(types), Types = types };
for (int a = 0; a < fs.Length; a++) { _dicTupleConstructor.AddOrUpdate(type, tupleInfo, (t2, c2) => tupleInfo);
types[a] = fs[a].FieldType; }
var read = ExecuteArrayRowReadClassOrTuple(types[a], names, row, dataIndex); var parms = new object[tupleInfo.Types.Length];
for (int a = 0; a < parms.Length; a++) {
var read = ExecuteArrayRowReadClassOrTuple(tupleInfo.Types[a], names, row, dataIndex);
if (read.dataIndex > dataIndex) dataIndex = read.dataIndex; if (read.dataIndex > dataIndex) dataIndex = read.dataIndex;
parms[a] = read.value; parms[a] = read.value;
} }
var constructor = type.GetConstructor(types); return (tupleInfo.Constructor?.Invoke(parms), dataIndex);
return (constructor?.Invoke(parms), dataIndex);
} }
return (dataIndex >= row.Length || (row[dataIndex] ?? DBNull.Value) == DBNull.Value ? null : GetDataReaderValue(type, row[dataIndex]), dataIndex + 1); return (dataIndex >= row.Length || (row[dataIndex] ?? DBNull.Value) == DBNull.Value ? null : GetDataReaderValue(type, row[dataIndex]), dataIndex + 1);
} }
@ -195,14 +206,18 @@ namespace FreeSql.Internal {
return (expando, names.Count); return (expando, names.Count);
} }
//类注入属性 //类注入属性
var value = type.GetConstructor(new Type[0]).Invoke(new object[0]); if (_dicClassConstructor.TryGetValue(type, out var classInfo)== false) {
var ps = type.GetProperties(); classInfo = new _dicClassConstructorInfo { Constructor = type.GetConstructor(new Type[0]), Properties = type.GetProperties() };
foreach(var p in ps) { _dicClassConstructor.TryAdd(type, classInfo);
}
var value = classInfo.Constructor.Invoke(new object[0]);
foreach(var prop in classInfo.Properties) {
var tryidx = dataIndex; var tryidx = dataIndex;
if (names != null && names.TryGetValue(p.Name, out tryidx) == false) continue; if (names != null && names.TryGetValue(prop.Name, out tryidx) == false) continue;
var read = ExecuteArrayRowReadClassOrTuple(p.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;
FillPropertyValue(value, p.Name, read.value); prop.SetValue(value, GetDataReaderValue(prop.PropertyType, read.value), null);
//FillPropertyValue(value, p.Name, read.value);
//p.SetValue(value, read.value); //p.SetValue(value, read.value);
} }
return (value, dataIndex); return (value, dataIndex);

View File

@ -9,6 +9,7 @@ namespace FreeSql.MySql {
class MySqlUtils : CommonUtils { class MySqlUtils : CommonUtils {
IFreeSql _orm; IFreeSql _orm;
public MySqlUtils(IFreeSql orm) { public MySqlUtils(IFreeSql orm) {
_orm = orm; _orm = orm;
} }
@ -72,5 +73,6 @@ namespace FreeSql.MySql {
} }
return columnName; return columnName;
} }
internal override string DbName => "MySql";
} }
} }

View File

@ -48,5 +48,6 @@ namespace FreeSql.Oracle {
internal override string QuoteWriteParamter(Type type, string paramterName) => paramterName; internal override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
internal override string QuoteReadColumn(Type type, string columnName) => columnName; internal override string QuoteReadColumn(Type type, string columnName) => columnName;
internal override string DbName => "Oracle";
} }
} }

View File

@ -104,5 +104,6 @@ namespace FreeSql.PostgreSQL {
internal override string QuoteWriteParamter(Type type, string paramterName) => paramterName; internal override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
internal override string QuoteReadColumn(Type type, string columnName) => columnName; internal override string QuoteReadColumn(Type type, string columnName) => columnName;
internal override string DbName => "PostgreSQL";
} }
} }

View File

@ -42,5 +42,6 @@ namespace FreeSql.SqlServer {
internal override string QuoteWriteParamter(Type type, string paramterName) => paramterName; internal override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
internal override string QuoteReadColumn(Type type, string columnName) => columnName; internal override string QuoteReadColumn(Type type, string columnName) => columnName;
internal override string DbName => "SqlServer";
} }
} }

View File

@ -63,5 +63,6 @@ namespace FreeSql.Sqlite {
internal override string QuoteWriteParamter(Type type, string paramterName) => paramterName; internal override string QuoteWriteParamter(Type type, string paramterName) => paramterName;
internal override string QuoteReadColumn(Type type, string columnName) => columnName; internal override string QuoteReadColumn(Type type, string columnName) => columnName;
internal override string DbName => "Sqlite";
} }
} }