如何构建属性选择器?
How to build a properties selector?
我正在尝试使用以下格式创建更新函数:
public int Update(
Item Item,
Expression<Func<Item, object>> selector)
如何读取选择器中选择的属性?我需要属性的名称。
这是因为我想让更新功能更智能,所以它只更新必须更新的内容。它用于 sql 并且它是一个存储库函数。
谢谢。
澄清更新:
我希望能够像这样调用更新:
Update(item, x => new { x.Property1, x.Property2 })
所以 IEnumerable of expressions 不是一个选项,但如果上面的更难,我会这样做...
假设您不想验证表达式实际上 return 一个 属性 值,这会起作用:
public int Update(Item item, params Expression<Func<Item, object>>[] selectors)
{
var propertyNames = selectors
.Select(expression => ((MemberExpression)expression.Body).Member.Name);
// ...
}
否则你需要类似于此的东西 comprehensive answer。
更新
如果你想使用匿名对象,那么你可以这样做:
public int Update<TProps>(Item item, Expression<Func<Item, TProps>> selector)
{
var propertyNames = typeof(TProps)
.GetProperties()
.Select(prop => prop.Name);
// ...
}
虽然这很容易被误用。
如果您不介意将更新代码写成 Update(item, x => x.Property1, x=> x.Property2 )
,它的可读性会更好,而且更容易编写,如 Johnathans 的回答所示。
尽管如果添加泛型,则可以强制 x 为同一类型并将其重新用于 Item
之外的不同对象
int Update<T>(T item, params Expression<Func<T, object>>[] selectors)
{
string getName(Expression e)
{
if(e is LambdaExpression l)
return getName(l.Body);
if(e is MemberExpression m)
return m.Member.Name;
if(e is UnaryExpression u)
return getName( u.Operand);
throw new NotImplementedException();
}
var names = selectors.Select(getName);
//update code...
}
注意,辅助函数是一个基本的,你可以使用更扩展的可重用辅助函数来获取名称,周围有很多
示例代码(第一个匿名对象只是为了创建一个示例对象):
Update(new { foo = "a", bar = 5}, x=>x.foo, x=>x.bar);
现在有趣的是,您可以通过删除 new 来强制使用单个表达式,或者您可以组合该函数,并且仍然允许匿名,方法是在辅助函数中添加对 NewExpression
的检查(当然要更改它以允许使用多个名称):
int Update<T>(T item, params Expression<Func<T, object>>[] selectors)
{
IEnumerable<string> getName(Expression e)
{
if (e is LambdaExpression l)
return getName(l.Body);
if (e is MemberExpression m)
return new[] { m.Member.Name };
if (e is UnaryExpression u)
return getName(u.Operand);
if (e is NewExpression n) // <- to account for the anonymous object
return n.Arguments.SelectMany(getName);
throw new NotImplementedException();
}
var names = selectors.SelectMany(getName);
// etc
}
这样你就可以选择(甚至混合)了。这两个调用产生相同的结果:
Update(new { foo = "a", bar = 5}, x=>x.foo, x=>x.bar);
Update(new { foo = "a", bar = 5 }, x => new { x.foo, x.bar});
我正在尝试使用以下格式创建更新函数:
public int Update(
Item Item,
Expression<Func<Item, object>> selector)
如何读取选择器中选择的属性?我需要属性的名称。
这是因为我想让更新功能更智能,所以它只更新必须更新的内容。它用于 sql 并且它是一个存储库函数。
谢谢。
澄清更新:
我希望能够像这样调用更新:
Update(item, x => new { x.Property1, x.Property2 })
所以 IEnumerable of expressions 不是一个选项,但如果上面的更难,我会这样做...
假设您不想验证表达式实际上 return 一个 属性 值,这会起作用:
public int Update(Item item, params Expression<Func<Item, object>>[] selectors)
{
var propertyNames = selectors
.Select(expression => ((MemberExpression)expression.Body).Member.Name);
// ...
}
否则你需要类似于此的东西 comprehensive answer。
更新
如果你想使用匿名对象,那么你可以这样做:
public int Update<TProps>(Item item, Expression<Func<Item, TProps>> selector)
{
var propertyNames = typeof(TProps)
.GetProperties()
.Select(prop => prop.Name);
// ...
}
虽然这很容易被误用。
如果您不介意将更新代码写成 Update(item, x => x.Property1, x=> x.Property2 )
,它的可读性会更好,而且更容易编写,如 Johnathans 的回答所示。
尽管如果添加泛型,则可以强制 x 为同一类型并将其重新用于 Item
int Update<T>(T item, params Expression<Func<T, object>>[] selectors)
{
string getName(Expression e)
{
if(e is LambdaExpression l)
return getName(l.Body);
if(e is MemberExpression m)
return m.Member.Name;
if(e is UnaryExpression u)
return getName( u.Operand);
throw new NotImplementedException();
}
var names = selectors.Select(getName);
//update code...
}
注意,辅助函数是一个基本的,你可以使用更扩展的可重用辅助函数来获取名称,周围有很多
示例代码(第一个匿名对象只是为了创建一个示例对象):
Update(new { foo = "a", bar = 5}, x=>x.foo, x=>x.bar);
现在有趣的是,您可以通过删除 new 来强制使用单个表达式,或者您可以组合该函数,并且仍然允许匿名,方法是在辅助函数中添加对 NewExpression
的检查(当然要更改它以允许使用多个名称):
int Update<T>(T item, params Expression<Func<T, object>>[] selectors)
{
IEnumerable<string> getName(Expression e)
{
if (e is LambdaExpression l)
return getName(l.Body);
if (e is MemberExpression m)
return new[] { m.Member.Name };
if (e is UnaryExpression u)
return getName(u.Operand);
if (e is NewExpression n) // <- to account for the anonymous object
return n.Arguments.SelectMany(getName);
throw new NotImplementedException();
}
var names = selectors.SelectMany(getName);
// etc
}
这样你就可以选择(甚至混合)了。这两个调用产生相同的结果:
Update(new { foo = "a", bar = 5}, x=>x.foo, x=>x.bar);
Update(new { foo = "a", bar = 5 }, x => new { x.foo, x.bar});