score:1

Accepted answer

You can easily do that using an ExpressionVisitor. Just create a new class and override the visiting methods. If you know that the projection was done using member bindings, you can simply override the method VisitMemberBinding and add the bound member to a list that you store as an instance variable. Then all you need to do is to make that instance variable public.

class ProjectionAnalyzer : ExpressionVisitor
{
    private HashSet<MemberInfo> boundMembers = new HashSet<MemberInfo>();

    protected override MemberBinding VisitMemberBinding(MemberBinding node)
    {
        boundMembers.Add(node.Member);
        return base.VisitMemberBinding(node);
    }

    public IEnumerable<MemberInfo> BoundMembers => boundMembers;
}

Then, use this class as follows:

var analyzer = new ProjectionAnalyzer();
analyzer.Visit(selectorPredicate);
var boundMembers = analyzer.BoundMembers;

How you obtain the selector predicate depends on your LINQ provider.

score:1

I did something similar using VisitMemberAssignment:

namespace BoundPropertiesinQuery
{
    static class IEnumerableExtensions
    {
        class ProjectedVisitor : ExpressionVisitor
        {
            public IList<string> ProjectedPropertyNames { get; set; } = new List<string>();

            protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
            {
                ProjectedPropertyNames.Add(node.Member.Name);
                return base.VisitMemberAssignment(node);
            }
        }

        public static IEnumerable<string> ProjectedProperties<T>(this IQueryable<T> @this)
        {
            var pv = new ProjectedVisitor();
            pv.Visit(@this.Expression);
            return pv.ProjectedPropertyNames.Distinct();
        }
    }

    internal class MyObject
    {
        public int Property1 { get; set; }
        public int Property2 { get; set; }

        public int Property3 { get; set; }

        public int Property4 { get; set; }
    }

    internal class MyOtherObject
    {
        public int other1 { get; set; }

        public int other2 { get; set; }

        public int other3 { get; set; }

        public int other4 { get; set; }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            var listOfItems = new List<MyOtherObject>()
            {
                new MyOtherObject
                {
                    other1 = 1,
                    other2 = 2,
                    other3 = 3,
                    other4 = 4
                },
                new MyOtherObject
                {
                    other1 = 5,
                    other2 = 6,
                    other3 = 7,
                    other4 = 8
                }
            };

           var result = listOfItems.AsQueryable().Select(m => new MyObject
               {
                   Property1 = m.other1,
                   Property2 = m.other2
               }).ProjectedProperties();

            foreach (var item in result)
            {
                Console.WriteLine(item);
            }

            Console.ReadLine();
        }
    }
}

Related Articles