获取表达式<func <t,object =“”>&gt;对于通过表达式<func <t,u =“”>&gt; </func <t,> </func <t,>访问的每个属性

时间:2015-01-19 15:48:09

标签: c# linq properties lambda

我有一个数据访问类,它充当逻辑类和底层数据源之间的中介,可以互换。此类允许您使用lambdas,LINQ样式查询数据源。与源无关的类提供高级功能,由几个基本操作(Add,GetAll,Update,Delete,Commit)提供支持,这些操作由小型适配器类实现,每个源类型一个(SQL,SQlite,XML序列化器,WCF客户端) ,REST客户端,等等。

我的问题是,一些关系数据源(特别是SQLite)在我需要时不够智能加载关系属性;我必须明确要求将它们包括在内。这适用于我的Get方法;我可以传递一个params数组表达式来加载我需要的任何东西。但是,对于.Any(),这感觉有点奇怪 - 如果我在询问是否有Customer列表包含某个项目的Purchases条记录,那么我不应该告诉它它加载Purchases列表;这似乎应该能够解决这个问题。

所以我的Any()方法需要Expression<Func<T, bool>>,其中T显然是我正在操作的类型。在上面的例子中,它将被使用如下:

using (var db = _dataAccessProvider.NewTransaction())
{
    return db.Any<Customer>(c => c.Purchases.Contains(someProduct));
}

是否可以使用代表操作Expression<Func<Customer, bool>>的{​​{1}}并确定它所引用的属性为c => c.Purchases.Contains(someProduct))?我该怎么做呢?一个涉及多个属性的lambda怎么样?

1 个答案:

答案 0 :(得分:2)

使用ExpressionVisitor查找引用所需对象属性的所有MemberExpression表达式。

快速举例:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

class Program
{
    sealed class ReferencedPropertyFinder : ExpressionVisitor
    {
        private readonly Type _ownerType;
        private readonly List<PropertyInfo> _properties = new List<PropertyInfo>();

        public ReferencedPropertyFinder(Type ownerType)
        {
            _ownerType = ownerType;
        }

        public IReadOnlyList<PropertyInfo> Properties
        {
            get { return _properties; }
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            var propertyInfo = node.Member as PropertyInfo;
            if(propertyInfo != null && _ownerType.IsAssignableFrom(propertyInfo.DeclaringType))
            {
                // probably more filtering required
                _properties.Add(propertyInfo);
            }
            return base.VisitMember(node);
        }
    }

    private static IReadOnlyList<PropertyInfo> GetReferencedProperties<T, U>(Expression<Func<T, U>> expression)
    {
        var v = new ReferencedPropertyFinder(typeof(T));
        v.Visit(expression);
        return v.Properties;
    }

    sealed class TestEntity
    {
        public int PropertyA { get; set; }

        public int PropertyB { get; set; }

        public int PropertyC { get; set; }
    }

    static void Main(string[] args)
    {
        Expression<Func<TestEntity, int>> expression =
            e => e.PropertyA + e.PropertyB;

        foreach(var property in GetReferencedProperties(expression))
        {
            Console.WriteLine(property.Name);
        }
    }
}