如何在ServiceStack或MLite中使用子查询

如何在ServiceStack或MLite中使用子查询,
Warning: implode(): Invalid arguments passed in /data/phpspider/zhask/webroot/tpl/detail.html on line 45
,,我正在使用ServiceStack ORMLite,需要执行以下查询: SqlServerExpressionVisitor<Contact> sql = new SqlServerExpressionVisitor<Contact>(); SqlServerExpressionVisitor<Account> accSql = new SqlServerExpressionVisitor<Account>(); var finalSql = s

我正在使用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());
SqlServerExpressionVisitor sql=newsqlserverexpressionvisitor();
SqlServerExpressionVisitor accSql=新SqlServerExpressionVisitor();
var finalSql=sql.Where(a=>
(来自accSql中的a1,其中a1.Type==“客户端”
&&a1.Id==a.AccountId选择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>();
    }
}
公共静态类SQL
{
公共静态bool ExistsIn(T值、字符串子查询、参数T[]参数)
{
返回true;
}
公共静态bool ExistsIn(T值,SqlExpressionVisitor innerSql)
{
返回true;
}
公共静态SqlExpressionVisitor Linq()
{
返回OrmLiteConfig.DialogProvider.ExpressionVisitor();
}
}
然后扩展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));
    }
}
内部对象VisitSQLMethodCall(MethodCallExpression m)
{
字符串methodName=m.Method.Name;
if(methodName==“ExistsIn”)
{
string quotedColName=Visit(m.Arguments[0]作为表达式).ToString();
动态访问=访问(m.Arguments[1]);
字符串innerQuery=(访问是字符串)?visit.ToString().Trim(“”):visit.ToSelectStatement();
if(m.Arguments[2]!=null)
{
列表字段=VisitePressionList((m.Arguments[2]作为NewArrayExpression).expression);
整数计数=0;
foreach(字段中的变量字段)
{
innerQuery=innerQuery.Replace(“@”+count.ToString(),field.ToString());
计数++;
}
}
返回新的PartialSqlString(string.Format({1})中的({0})”,quotedColName,innerQuery);
}
}
结果非常有希望,下面是它的使用方法:

.Where(a => SQL.ExistsIn(a.AccountId, SQL.Linq<Account>()
    .Where(acc => acc.Name.Contains("a")).Select(acc => acc.Id)))
.Where(a=>SQL.ExistsIn(a.AccountId,SQL.Linq()
。其中(acc=>acc.Name.Contains(“a”))。选择(acc=>acc.Id)))
上面生成了正确的内部SQL,但是如果我包含来自父查询的引用,系统将再次调用返回表达式.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)))
SQL.Linq().Where(a=>SQL.ExistsIn(a.AccountId,SQL.Linq())
。其中(acc=>acc.Id==a.AccountId)。选择(acc=>acc.Id)))
上述操作会产生错误:范围中未定义参数“a”。 我想我只需要在自定义方法中的第二次访问调用中添加一个参数定义,但我还没有弄清楚如何添加!
非常感谢您的帮助!

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;
        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());
var jb=new JoinSqlBuilder()
.Join(x=>x.AccountId,x=>x.Id)
.Where以及Ormlite。由于我扩展了Ormlite的T4以自动生成列名Table,因此我使用DbExtension如下:

        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;
        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());
SqlBuilder=newsqlbuilder();
builder=builder。选择(“*”)。
FROM(Contact.TABLE_NAME).WHERE(Contact.COLUMN_AccountId+“=”+id.ToString());
var sql=builder.ToString();
返回dbConnection.FirstOrDefault(sql,builder.ParameterValues.ToArrayEx());

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;
        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());
var jb=new JoinSqlBuilder()
.Join(x=>x.AccountId,x=>x.Id)
.Where以及Ormlite。由于我扩展了Ormlite的T4以自动生成列名Table,因此我使用DbExtension如下:

        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;
        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());
SqlBuilder=newsqlbuilder();
builder=builder。选择(“*”)。
FROM(Contact.TABLE_NAME).WHERE(Contact.COLUMN_AccountId+“=”+id.ToString());
var sql=builder.ToString();
返回dbConnection.FirstOrDefault(sql,builder.ParameterValues.ToArrayEx());

虽然我知道ORMLite的新版本可以处理连接,但我更愿意使用另一种ORM,如Dapper来解决两个或多个表之间的连接。我发现Dapper在这方面更强大。我还没有看到任何关于Dapper的文档支持Linq到SQL。而且我觉得我更了解ORMLite代码,它写得非常好y、 虽然我知道ORMLite的新版本可以处理连接,但我更愿意使用另一个ORM(如Dapper)来解决两个或多个表之间的连接。我发现Dapper在这方面更强大。我还没有看到任何关于Dapper的支持Linq to SQL的文档。另外,我觉得我更了解ORMLite代码,而且它写得非常好作为更多的人,我正在寻找一种将所有内容都与SqlServerExpressionVisitor联系起来的解决方案。就API而言,如果我想让其他开发人员使用它,我宁愿只使用一个API流。如果开发人员需要走两条不同的路径,一条是使用SqlServerExpressionVisitor,另一条是JoinSqlBuilder。另外,对于我需要做的事情,不要使用join是必需的。我只需要子查询。我不确定EF是如何做到这一点的,但通过完整的IQueryable接口,他们已经实现了所有这一切。如果您只是想使用ExpressionVisitor,很遗憾,您无法使用子查询。EF的工作方式是它解析子表达式并生成SQL。如果您只想使用Linq bas我建议您看看linq2db-&&我更希望找到一种将所有内容都绑定到SqlServerExpressionVisi的解决方案