C# 遍历表达式树提取表名

msi*_*man 1 expression-trees linq-to-sql

我需要一种方法来遍历 LINQ-to-SQL 表达式树以提取查询中的表名称。即使只是查询中使用的第一个表可能就足够了。

例子:

var query = from c in Db.Customers select c;
Run Code Online (Sandbox Code Playgroud)

以及理想的函数:

string TableName = ExtractTablesFromQuery(query);
Run Code Online (Sandbox Code Playgroud)

将返回字符串“Customers”

Dam*_*enG 5

LINQ to SQL 不会向您公开此功能,因此您有两个选择。

使用 dataContext.GetCommand(myQuery) 函数并解析 TSQL

这对于连接等可能会有点棘手,但可以保证您获得将要涉及的确切表名称。

自己访问表达式树

这并不太困难,但存在的问题是 LINQ to SQL 会推断并优化实际使用的表,因此您无法获得 100% 准确的结果。例如,如果您加入了一个表但没有返回任何结果,它将被优化,但您不会通过访问表达式树知道这一点,除非您完全像 LINQ to SQL 那样进行优化(这将是大量工作) 。

如果你无论如何都想尝试#2,这里有一个可以帮助你开始的例子:

public static class TableFinder
{
    public static IEnumerable<string> GetTableNames(this DataContext context, IQueryable queryable) {
        var visitor = new TableFindingVisitor(context.Mapping);
        visitor.Visit(queryable.Expression);
        return visitor.Tables.Select(t => t.TableName).Distinct().AsEnumerable();
    }

    class TableFindingVisitor : ExpressionVisitor
    {
        private readonly HashSet<MetaTable> foundTables = new HashSet<MetaTable>();
        private readonly MetaModel mapping;

        public TableFindingVisitor(MetaModel mapping) {
            this.mapping = mapping;
        }

        public override Expression Visit(Expression node) {
            return base.Visit(node);
        }

        protected override Expression VisitConstant(ConstantExpression node) {
            if (node.Type.GetGenericTypeDefinition() == typeof(Table<>))
                CheckType(node.Type.GetGenericArguments()[0]);
            return base.VisitConstant(node);
        }

        protected override Expression VisitMember(MemberExpression node) {
            CheckType(node.Member.DeclaringType);
            return base.VisitMember(node);
        }

        public IEnumerable<MetaTable> Tables { get { return foundTables; } }

        private void CheckType(Type t) {
            var table = mapping.GetTable(t);
            if (table != null && !foundTables.Contains(table))
                foundTables.Add(table);
        }
    }
Run Code Online (Sandbox Code Playgroud)

要使用它,您需要对 dataContext.GetTables(myQuery); 的结果进行 foreach 操作。