通过Expression初始化对象获取赋值内容,用到不完整的ORM中

_

前言

现在这边公司有着自己的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

剩下的问题就是怎么去解析内容和列名映射问题了

方式很简单,只要注意几个点就能找到解决思路

1Expression存在多种类型,我们需要对他进行强转才能获取到内容

2.按照目标的方式传参,表达式类型MemberInit,这个可以通过Type获取到,算是切入点

3.列名映射一般Attribute实现,这可以通过表达式Member.CustomAttributes获取

4.注意不同类型Expression结构和参数获取方式

## 代码如下

<details>

<summary>表达式解析</summary>

```c#

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}");

}

}

```

</details>

自用组装批量入库SQL方法 2022-03-30
C# List分页 2022-04-22