一直以来自己从数据库取数据,都是遍历SqlDataReader。通过SqlDataReader的string索引器获取数据。
后来自己研究反射,可以动态的将SqlDataReader的数据赋值给泛型T。省了不少收写代码的工作。但是随着业务量的增加,一直使用反射就会有性能和资源的损耗。为了解决这个问题。就想到了缓存+反射+表达式目录树。把已经赋值过的泛型T,缓存起来,下次找缓存,如果有赋值过具体T类型,就直接取缓存的目录树,然后Compile().Invoke()。这就解决了性能损耗和代码量的问题。
这个是以前的方法,避免不了性能损耗问题
public List<T> FindAll<T>() where T : BaseModel
{
Type type = typeof(T);
List<T> list = new List<T>();
//var Parameter = new SqlParameter("@Id", Id);
using (SqlConnection connection = new SqlConnection(StaticField.Connection))
{
SqlCommand command = new SqlCommand(Cache<T>.DeleteSql, connection);
//command.Parameters.Add(Parameter);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
var t = Activator.CreateInstance(type) as T;
foreach (var item in type.GetProperties())
{
item.SetValue(item.Name, reader[$"{item.Name }"]==DBNull.Value?null: reader[$"{item.Name }"]);
}
list.Add(t);
}
connection.Close();
return list;
}
}
先解释一下什么是表达式目录树,其实就是把表达式理解成一个树。组装的时候,一个叶一个叶的取组装。拆解的时候一个一个的拆。(从右往左拆或者组装)就比如说表达式
x.name==5&&x.Age>3。可以拆成x/、name、&&、 x 、age 。这些具体的元素都能找到表达式目录树里面对应的表示方法。具体的方法你们可以研究一下Expression类。里面有方法、属性、字段等等的组装方法。拿上面那个做个例子:
ParameterExpression parameter = Expression.Parameter(typeof(具体类型), "P");这个P就是给这个委托给起了个别名(统一了一个名字)
x.name这个name是个属性。所以就添加p的name属性
PropertyInfo Info = typeof(具体类型).GetProperty("name");
MemberExpression member = Expression.Property(parameter, Info);//把p和name拼起来就成了p.name.
表达式目录树里面有表示==、&&、>、乘除等等等。反正都能拼接起来。很多方法,我研究了很长时间,才研究了几个方法。你们自己探究
这个是使用表达式目录树的方式。自己在添加一个缓存,把已经通过目录树赋值过的缓存起来,下次来的时候,直接用。审了不少性能问题
public TOut ExpressionConvertToModel<TOut>(SqlDataReader tin)
{
ParameterExpression parameter = Expression.Parameter(typeof(SqlDataReader), "P");
List<MemberBinding> list = new List<MemberBinding>();
foreach (var item in typeof(TOut).GetProperties())
{
string Info = tin[item.Name].ToString();
if (Info != null)
{
//PropertyInfo Infos = tin.GetType().GetProperty("item_get");索引器就是调用对象属性的item_get方法。
var In = tin.GetType().GetProperties().Where(m => m.GetIndexParameters().Length > 0).ToArray()[1];//获取string索引器
//MemberExpression member = Expression.Property(parameter, In);//p.属性名
//下一步就是拼接这个SqlDataReader[]
var methodCall = Expression.Call(parameter,In.GetMethod, Expression.Constant(item.Name));//call就是调用方法,都一个是p然后是p.Get_item()这个表示索引器的get方法,最后一个是参数
MethodInfo containsMethod = typeof(object).GetMethod("ToString");
if (typeof(string) == item.PropertyType)
{
containsMethod = typeof(object).GetMethod("ToString");//using System.Reflection;ToString方法
}
else if (typeof(bool) == item.PropertyType)
{
//下面自己去扩展
}
else if (typeof(byte) == item.PropertyType)
{
}
else if (typeof(char) == item.PropertyType)
{
}
else if (typeof(decimal) == item.PropertyType)
{
}
else if (typeof(double) == item.PropertyType)
{
}
else if (typeof(float) == item.PropertyType)
{
}
else if (typeof(long) == item.PropertyType)
{
}
else if (typeof(sbyte) == item.PropertyType)
{
}
else if (typeof(short) == item.PropertyType)
{
}
else if (typeof(uint) == item.PropertyType)
{
}
containsMethod = typeof(object).GetMethod("ToString");//T获取object的Tostring方法
var methodCal2 = Expression.Call(methodCall, containsMethod);//调用Tostring方法
//var methodCal2 = Expression.ArrayIndex(methodCall, Expression.Constant(item.Name));
//var methodCall = Expression.Call(parameter,In.GetMethod ?? throw new InvalidOperationException(), Expression.Constant(item.Name));
MemberBinding member1 = Expression.Bind(item, methodCal2);//p.属性名=属性名
list.Add(member1);
}
}
MemberInitExpression memberInit = Expression.MemberInit(Expression.New(typeof(TOut)), list.ToArray());
Expression<Func<SqlDataReader, TOut>> expression = Expression.Lambda<Func<SqlDataReader, TOut>>(memberInit, new ParameterExpression[] { parameter });
return expression.Compile().Invoke(tin);
}