如何在ServiceStack ORMLite中使用子查询

时间:2013-08-21 03:31:52

标签: linq-to-sql servicestack ormlite-servicestack

我正在使用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”这里是对父方法调用定义的变量的引用。

如何使用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)))

Above生成正确的内部SQL,但是如果我包含来自父Query的引用,系统再次调用返回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”。 我想我只需要以某种方式在我的自定义方法中为第二个访问调用添加一个参数定义,但我还没弄明白怎么做! 任何帮助表示赞赏!

1 个答案:

答案 0 :(得分:4)

Wiki中没有文档;您需要查看代码以了解如何使用连接功能。对于您的问题,代码应如下所示:

        var jb = new JoinSqlBuilder<Contact, Account>()
            .Join<Contact, Account>(x => x.AccountId, x => x.Id)
            .Where<Account>(x => x.Type == "Client")
            .SelectCount<Contact>(x => x.Id);
        var sqlStr = jb.ToSql();
        bool isAvailable = dbManager.Connection.SqlScalar<int>(sqlStr) > 0;

对于复杂子查询,当前版本的JoinBuilder无用。我个人使用http://www.nuget.org/packages/DbExtensions/的DbExtensions和Ormlite。由于我已经扩展了Ormlite的T4以自动生成列名,Table,我使用DbExtension,如下所示:

        SqlBuilder builder = new SqlBuilder();
        builder = builder.SELECT("*").
                FROM(Contact.TABLE_NAME).WHERE(Contact.COLUMN_AccountId +" = " + id.ToString());
        var sql = builder.ToString();
        return dbConnection.FirstOrDefault<Contact>(sql, builder.ParameterValues.ToArrayEx());