Search code examples
c#linqiqueryableexpressionvisitor

If Participating in Queryable c# join


Looking for ways to find participation in queryable Joins...

Basically, I want to check if model class X is used in join operations in Queryable statements. Using David Fowl's QueryInterceptor I can put an expression visitor on an IQueryable. Then check for Lambda expressions (looks like joins are represented with them generally) for parameters with a type of class X. The visitor can toggle a flag for a hit.

Other ways? Again, I am only interested in if class X is involved in joins.


Solution

  • You can use the ExpressionVisitor class to inspect the Expression Tree and pull out the generic types of the join.

    In the static constructor we will use reflection to find the method infos for the Join on the Queryable class. On the method calls we will see if the call method is generic and if it matches the Join method on the Queryable Class. If so we know the first two generic arguments are the Outer Type and Inner Type. Add them to a hashset just to remove any duplicates.

    public class JoinVisitor : ExpressionVisitor
    {
        private static readonly MemberInfo[] _joinMethods;
    
        private ICollection<Type> Types = new HashSet<Type>();
    
        static JoinVisitor()
        {
            _joinMethods =
                typeof (Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(m => m.Name == "Join").ToArray();
    
        }
    
        // make use of GetJoinTypes to create class
        private JoinVisitor()
        {
    
        }
    
        public static IEnumerable<Type> GetJoinTypes(System.Linq.Expressions.Expression expression)
        {
            var joinVisitor = new JoinVisitor();
            joinVisitor.Visit(expression);
            return joinVisitor.Types;
        }
    
        protected override System.Linq.Expressions.Expression VisitMethodCall(MethodCallExpression node)
        {
            if (node.Method.IsGenericMethod && _joinMethods.Contains(node.Method.GetGenericMethodDefinition()))
            {
                var args = node.Method.GetGenericArguments();
                Types.Add(args[0]);
                Types.Add(args[1]);
            }
            return base.VisitMethodCall(node);
        }
    }
    

    Can use it like

    IQueryable queryable ;  // your IQueryable 
    var types = JoinVisitor.GetJoinTypes(queryable.Expression);
    

    Then use can just use the contains method to see if the type is in the join. You could also change this to pass in the type GetJoinTypes and have it return bool but I usually write them this way to make it more flexible.