我正在使用 ServiceStack ORMLite,需要执行如下查询:
SqlServerExpressionVisitor<Contact> sql = new SqlServerExpressionVisitor<Contact>();
SqlServerExpressionVisitor<Account> accSql = new SqlServerExpressionVisitor<Account>();
var finalSql = sql.Where(a=>
(from a1 in accSql where a1.Type == "Client"
&& a1.Id==a.AccountId select a1).Any());
执行此查询时,我得到一个 lambda 错误“a”未在范围内定义。这里的“a”是对父 Where 方法调用定义的变量的引用。
如何使用 ExpressionVisitor 在 WHERE 子句中执行子查询?
更新:我创建了自己的 SqlServiceExpressionVisitor,它允许我自定义 ORM 如何生成 SQL 语句。我还添加了如下类:
public static class SQL
{
public static bool ExistsIn<T>(T Value, string subquery, params T[] parameters)
{
return true;
}
public static bool ExistsIn<T, TItem>(T Value, SqlExpressionVisitor<TItem> innerSql)
{
return true;
}
public static SqlExpressionVisitor<T> Linq<T>()
{
return OrmLiteConfig.DialectProvider.ExpressionVisitor<T>();
}
}
然后扩展 VisitMethodCall 以考虑我的新类并相应地调用我的自定义方法:
internal object VisitSQLMethodCall(MethodCallExpression m)
{
string methodName = m.Method.Name;
if (methodName == "ExistsIn")
{
string quotedColName = Visit(m.Arguments[0] as Expression).ToString();
dynamic visit = Visit(m.Arguments[1]);
string innerQuery = (visit is string) ? visit.ToString().Trim('"') : visit.ToSelectStatement();
if (m.Arguments[2] != null)
{
List<object> fields = VisitExpressionList((m.Arguments[2] as NewArrayExpression).Expressions);
int count = 0;
foreach (var field in fields)
{
innerQuery = innerQuery.Replace("@" + count.ToString(), field.ToString());
count++;
}
}
return new PartialSqlString(string.Format("{0} IN ({1})", quotedColName, innerQuery));
}
}
结果非常有希望,以下是它的使用方法:
.Where(a => SQL.ExistsIn(a.AccountId, SQL.Linq<Account>()
.Where(acc => acc.Name.Contains("a")).Select(acc => acc.Id)))
上面生成了正确的内部 SQL,但是如果我包含来自父查询的引用,系统再次调用 return Expression.Lambda(m).Compile().DynamicInvoke(); 产生相同的错误!
SQL.Linq<Contact>().Where(a => SQL.ExistsIn(a.AccountId, SQL.Linq<Account>()
.Where(acc => acc.Id == a.AccountId).Select(acc => acc.Id)))
以上产生错误:参数“a”未在范围内定义。我想我只需要以某种方式在我的自定义方法中的第二次访问调用中添加一个参数定义,但我还没有弄清楚怎么做!任何帮助表示赞赏!