前言
现在这边公司有着自己的ORM,对Update函数的设计是给一个匿名对象进行数据更新操作
int Update(object entity, Expression<Func<TEntity, bool>> expr);
int Update(Hashtable hashTable, Expression<Func<TEntity, bool>> expr);
因此在使用的时候,不少人遇到变量名称跟数据库字段不一致导致数据更新不了,编译还不会报错,甚至出现上线了才发现的情况,我也在联调接口的时候踩过一次坑.
近期我这边组长在测试环境因为这个喜提Bug一个
因此,我想着用Expression
封装一下,让开发能在VS中方便填写属性名和编译异常隔绝这种问题
以前用过类似的约束,使用方式大致如下,其Name限定是了Class的属性了
_rep.Update(x=>new Class{Name="NewName"});
这种约束刚好能解决匿名变量导致字段不对的情况,因此按照这个效果搞一个扩展函数
留下文章,好方便自己以后CV
主要思路
因为比较喜欢Lambda
,加上肯定是泛型兼容,那基本躲不开Expression
剩下的问题就是怎么去解析内容和列名映射问题了
方式很简单,只要注意几个点就能找到解决思路
1.Expression
存在多种类型,我们需要对他进行强转才能获取到内容
2.按照目标的方式传参,表达式类型是MemberInit
,这个可以通过Type获取到,算是切入点
3.列名映射一般用Attribute
实现,这可以通过表达式中Member.CustomAttributes
获取
4.注意不同类型的Expression
结构和参数获取方式
代码如下
表达式解析
public static int Update<T>(this ITable<T> itable, Expression<Func<T, T>> expressions, Expression<Func<T, bool>> expr)
{
if (expressions.Body.NodeType != ExpressionType.MemberInit)
throw new NotSupportedException("Just Supported MemberInit Expression");
var hashtable = new System.Collections.Hashtable();
var initExp = expressions.Body as MemberInitExpression;
var bindings = initExp.Bindings;
foreach (MemberAssignment item in bindings)
{
var keyName = item.Member.Name;
if (item.Member.CustomAttributes.Any())
{
var namedArgs = item.Member.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(ColumnAttribute) && x.NamedArguments.Any(y => y.MemberName == nameof(ColumnAttribute.ColumnName)));
if (namedArgs != null)
keyName = namedArgs.NamedArguments.FirstOrDefault().TypedValue.Value.ToString();
}
hashtable.Add(keyName, Evaluate(item.Expression));
}
if (hashtable.Count > 0)
return itable.Update(hashtable, expr);
return -1;
}
private static object Evaluate(Expression expr)
{
switch (expr.NodeType)
{
case ExpressionType.Constant:
return ((ConstantExpression)expr).Value;
case ExpressionType.MemberAccess:
var me = (MemberExpression)expr;
object target = me.Expression == null ? "" : Evaluate(me.Expression);
switch (me.Member.MemberType)
{
case System.Reflection.MemberTypes.Field:
return ((System.Reflection.FieldInfo)me.Member).GetValue(target);
case System.Reflection.MemberTypes.Property:
return ((System.Reflection.PropertyInfo)me.Member).GetValue(target, null);
default:
throw new NotSupportedException($"Not Supported MemberType of Update Helper:{me.Member.MemberType}");
}
case ExpressionType.Conditional:
var ce = (ConditionalExpression)expr;
var boolResult = (bool)Expression.Lambda(ce.Test).Compile().DynamicInvoke();
return boolResult ? Evaluate(ce.IfTrue) : Evaluate(ce.IfFalse);
case ExpressionType.Convert:
var ue = (UnaryExpression)expr;
//if (ue.Type.IsEnum)
return Expression.Lambda(ue).Compile().DynamicInvoke();
throw new NotSupportedException("Not Supported this Convert Analysis");
default:
throw new NotSupportedException($"Not Supported NodeType of Update Helper:{expr.NodeType}");
}
}
Q.E.D.