使用表达式更新实体 属性 如果不为空

Update Entity Property if Not Null Using Expressions

我一直在尝试创建一个通用函数来检查值是否为空,否则它将实体上的 属性 设置为该值。看起来很简单,以为我有它,但是当 属性 的类型不可为空(int)但值的类型显然可以为空(int?)时遇到了一些问题。字符串工作正常,因为它们无论如何都可以为空。

示例:

UpdateEntityPropertyIfNotNull(entity, entity => entity.Id, request.Id);

那么这里的entityId是int类型,而requestId值是int类型? => 通用函数最终只是认为它们都是 int?

这是我制作通用函数的尝试:

private void UpdateEntityPropertyIfNotNull<T, TProperty>(T entity, Expression<Func<T, TProperty>> propertyFunc, TProperty valueToSet)
{
    if (valueToSet == null) 
    {
        return;
    }

    //if (propertyFunc.Body.NodeType != ExpressionType.MemberAccess)
    //{
    //    throw new ArgumentException("This should be a member getter", nameof(propertyFunc));
    //}

    MemberExpression? member;
    switch (propertyFunc.Body.NodeType)
    {
        case ExpressionType.Convert:
        case ExpressionType.ConvertChecked:
            var unary = propertyFunc.Body as UnaryExpression;
            member = (unary?.Operand) as MemberExpression;
            break;
        default:
            member = propertyFunc.Body as MemberExpression;
            break;
    }

    var underlyingType = Nullable.GetUnderlyingType(typeof(TProperty)) ?? typeof(TProperty);
    var model = propertyFunc.Parameters[0];
    var value = Expression.Variable(underlyingType, nameof(model));
    var assignment = Expression.Assign(member, value);
    var lambdaExpression = Expression.Lambda<Action<T, TProperty>>(assignment, model, value).Compile();
    lambdaExpression(entity, valueToSet);
}

任何建议都很好,我讨厌必须手动进行条件检查的想法...

您需要将约束组合在一起,本质上创建一个可为空的值类型约束,以及另一个:

private void UpdateEntityPropertyIfNotNull<T, TProperty>(T entity, Expression<Func<T, TProperty>> propertyFunc, TProperty valueToSet)
{
    if (!typeof(TProperty).IsValueType && valueToSet is null)
    {
        return;
    }
    
    UpdateEntityProperty(entity, propertyFunc, valueToSet);
}

private void UpdateEntityPropertyIfNotNull<T, TProperty>(T entity, Expression<Func<T, TProperty>> propertyFunc, TProperty? valueToSet)
    where TProperty : struct
{
    if (!valueToSet.HasValue)
    {
        return;
    }

    UpdateEntityProperty(entity, propertyFunc, valueToSet.Value);
}

private void UpdateEntityPropertyIfNotNull<T, TProperty>(T entity, Expression<Func<T, TProperty?>> propertyFunc, TProperty? valueToSet)
    where TProperty : struct
{
    if (!valueToSet.HasValue)
    {
        return;
    }
    
    UpdateEntityProperty(entity, propertyFunc, valueToSet.Value);
}

private void UpdateEntityProperty<T, TProperty>(T entity, Expression<Func<T, TProperty>> propertyFunc, TProperty valueToSet)
{
    // snip
}

您获得 compile-time 正确处理参数的有效性,它包含 reference-types、value-types 和可为空 value-types。

最后的改动是将UpdateEntityProperty<T, TProperty>()更新为

var underlyingType = Nullable.GetUnderlyingType(typeof(TProperty)) ?? typeof(TProperty);

var underlyingType = typeof(TProperty);

或删除它并直接使用 typeof():

var value = Expression.Variable(typeof(TProperty), nameof(model));