【问题标题】:How to build a properties selector?如何构建属性选择器?
【发布时间】:2020-05-19 10:29:24
【问题描述】:

我正在尝试使用以下格式创建更新函数:

public int Update(
        Item Item, 
        Expression<Func<Item, object>> selector)

如何读取选择器中选择的属性?我需要属性的名称。

这是因为我想让更新功能更智能,所以它只更新必须更新的内容。它是用于 sql 的,它是一个存储库函数。

谢谢。

更新说明:

我希望能够像这样调用更新:

Update(item, x => new { x.Property1, x.Property2 })

所以表达式的 IEnumerable 不是一个选项,但如果上述内容更难,我会这样做......

【问题讨论】:

  • 这能回答你的问题吗? Retrieving Property name from lambda expression
  • 否,因为我想从同一个表达式中选择多个属性名称,而不仅仅是一个。
  • 然后你可以传递一个IEnumerable&lt;Expression&lt;Func&lt;Item, object&gt;&gt;&gt; 并迭代,甚至params Expression&lt;Func&lt;Item, object&gt;&gt;[]

标签: c# sql .net repository-pattern


【解决方案1】:

假设您不想验证表达式是否确实返回了属性值,这将起作用:

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

    // ...
}

虽然这很容易被滥用。

【讨论】:

    【解决方案2】:

    如果您不介意将更新代码编写为 Update(item, x =&gt; x.Property1, x=&gt; 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=&gt;x.foo, x=&gt;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});
    

    【讨论】:

    • 我使用了你在帮助类中实现的方法,谢谢!
    猜你喜欢
    • 2021-07-11
    • 2019-03-02
    • 1970-01-01
    • 1970-01-01
    • 2021-01-31
    • 1970-01-01
    • 2015-12-28
    • 1970-01-01
    • 2014-01-10
    相关资源
    最近更新 更多