使用表达式更新实体 属性 如果不为空
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));
我一直在尝试创建一个通用函数来检查值是否为空,否则它将实体上的 属性 设置为该值。看起来很简单,以为我有它,但是当 属性 的类型不可为空(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));