我需要一种遍历LINQ-to-SQL表达式树的方法来提取查询中的表名。即使只是查询中使用的第一个表也可能就足够了。
示例:
var query = from c in Db.Customers select c;
理想的功能:
string TableName = ExtractTablesFromQuery(query);
会返回字符串“Customers”
答案 0 :(得分:4)
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);
}
}
要使用此功能,您需要预先处理dataContext.GetTables(myQuery)的结果;