前言

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


随意游世