NHibernate-使用计数的Linq查询(不同)

NHibernate-使用计数的Linq查询(不同),nhibernate,linq-to-nhibernate,Nhibernate,Linq To Nhibernate,我正在尝试使用LINQ和NHibernate使分页查询正常工作。在一个表上,这是完美的,但是当多个表被连接时,这会给我带来很大的麻烦。这是我到目前为止所拥有的 public virtual PagedList<Provider> GetPagedProviders(int startIndex, int count, System.Linq.Expressions.Expression<Func<Record, bool>> predicate) {

我正在尝试使用LINQ和NHibernate使分页查询正常工作。在一个表上,这是完美的,但是当多个表被连接时,这会给我带来很大的麻烦。这是我到目前为止所拥有的

public virtual PagedList<Provider> GetPagedProviders(int startIndex, int count, System.Linq.Expressions.Expression<Func<Record, bool>> predicate) {

    var firstResult = startIndex == 1 ? 0 : (startIndex - 1) * count;

    var query = (from p in session.Query<Record>()
                .Where(predicate)
                select p.Provider);

    var rowCount = query.Select(x => x.Id).Distinct().Count();

    var pageOfItems = query.Distinct().Skip(firstResult).Take(count).ToList<Provider>();
    return new PagedList<Provider>(pageOfItems, startIndex, count, rowCount);
} 
第二个查询的问题是“distinct”关键字不存在于外部查询中,而只存在于内部查询中。有没有办法纠正这个问题

谢谢你的建议

[更新]

这是构建Linq查询谓词的代码

private Expression<Func<Record, bool>> ParseQueryExpression(string Query) {
            Expression<Func<Record, bool>> mExpression = x => true;
            string[] splitQuery = Query.Split('|');
            foreach (string query in splitQuery) {
                if (string.IsNullOrEmpty(query))
                    continue;
                int valStartIndex = query.IndexOf('(');
                string variable = query.Substring(0, valStartIndex);
                string value = query.Substring(valStartIndex + 1, query.IndexOf(')') - valStartIndex - 1);

                switch (variable) {
                    case "tax":
                        mExpression = x => x.Provider.TaxID == value;
                        break;
                    case "net":
                        mExpression = Combine<Record>(mExpression, x => x.Network.Id == int.Parse(value));
                        break;
                    case "con":
                        mExpression = Combine<Record>(mExpression, x => x.Contract.Id == int.Parse(value));
                        break;
                    case "eff":
                        mExpression = Combine<Record>(mExpression, x => x.Effective_Date >= DateTime.Parse(value));
                        break;
                    case "trm":
                        mExpression = Combine<Record>(mExpression, x => x.Term_Date <= DateTime.Parse(value));
                        break;
                    case "pid":
                        mExpression = Combine<Record>(mExpression, x => x.Provider.Id == long.Parse(value));
                        break;
                    case "rid":
                        mExpression = Combine<Record>(mExpression, x => x.Rate.Id == int.Parse(value));
                        break;                        
                }
            }
            return mExpression;
        }
专用表达式ParseQueryExpression(字符串查询){
表达式mExpression=x=>true;
string[]splitQuery=Query.Split(“|”);
foreach(splitQuery中的字符串查询){
if(string.IsNullOrEmpty(查询))
继续;
int valStartIndex=query.IndexOf('(');
字符串变量=query.Substring(0,valStartIndex);
字符串值=query.Substring(valStartIndex+1,query.IndexOf('))-valStartIndex-1);
开关(可变){
案例“税”:
mExpression=x=>x.Provider.TaxID==value;
打破
案件“净额”:
mExpression=Combine(mExpression,x=>x.Network.Id==int.Parse(value));
打破
案例“con”:
mExpression=Combine(mExpression,x=>x.Contract.Id==int.Parse(value));
打破
案例“eff”:
mExpression=Combine(mExpression,x=>x.Effective\u Date>=DateTime.Parse(value));
打破
案例“trm”:
mExpression=Combine(mExpression,x=>x.Term\u Date x.Provider.Id==long.Parse(value));
打破
案例“rid”:
mExpression=Combine(mExpression,x=>x.Rate.Id==int.Parse(value));
打破
}
}
返回表达式;
}

Linq提供者似乎有一些“意外”行为。我可以用您描述的行数重现这个问题,并且在尝试用子查询修复它时也遇到了一些问题

如果我正确理解您的意图,您基本上希望有一个记录符合特定标准的供应商的分页列表。 在这种情况下,我建议使用子查询。但是,我尝试使用
Query()
方法实现子查询,但没有成功。因此,我尝试了
QueryOver()
方法,该方法工作完美。在您的情况下,所需的查询如下所示:

Provider pAlias = null;
var query = session.QueryOver<Provider>(() => pAlias).WithSubquery
                        .WhereExists(QueryOver.Of<Record>()
                            .Where(predicate)
                            .And(r => r.Provider.Id == pAlias.Id)
                            .Select(r => r.Provider));

var rowCount = query.RowCount();

var pageOfItems = query.Skip(firstResult).Take(count).List<Provider>();
这里的问题是您有多个查询,因此有多个到数据库的往返。当您检查生成的sql时,会出现另一个问题。子查询
idList.Contains(p.Id)
将生成一个包含大量参数的in子句

如您所见,这些是NHibernate.Linq查询。这是因为新的QueryOver特性不是真正的Linq提供程序,并且不能很好地处理动态Linq表达式

您可以通过使用
分离的查询器
绕过这个限制。这里的缺点是您必须以某种方式修改
ParseQueryExpression()
,例如:

private QueryOver<Record> ParseQueryExpression(string Query)
{
    Record rAlias = null;
    var detachedQueryOver = QueryOver.Of<Record>(() => rAlias)
                .JoinQueryOver(r => r.Provider)
                .Where(() => rAlias.Provider.TaxID == "000000000")
                .Select(x => x.Id);
    // modify your method to match the return value
    return detachedQueryOver;
}

// in your main method
var detachedQueryOver = QueryOver.Of<Record>()
    .WithSubquery
    .WhereProperty(r => r.Id
    .In(this.ParseQueryExpression(...));

var queryOverList = session.QueryOver<Provider>()
    .WithSubquery
    .WhereProperty(x => x.Id)
    .In(detachedQueryOver.Select(r => r.Provider.Id));

int rowCount = queryOverList.RowCount();
var pageOfItems = queryOverList.Skip(firstResult).Take(count).List();
private QueryOver ParseQueryExpression(字符串查询)
{
记录为空;
var detachedQueryOver=QueryOver.Of(()=>rAlias)
.JoinQueryOver(r=>r.Provider)
.Where(()=>rAlias.Provider.TaxID==“000000000”)
.选择(x=>x.Id);
//修改方法以匹配返回值
返回分离查询器;
}
//在你的主要方法中
var detachedQueryOver=QueryOver.Of()
.带subquery
.WhereProperty(r=>r.Id
.In(this.ParseQueryExpression(…);
var queryOverList=session.QueryOver()
.带subquery
.WhereProperty(x=>x.Id)
.In(detachedQueryOver.Select(r=>r.Provider.Id));
int rowCount=queryOverList.rowCount();
var pageOfItems=queryOverList.Skip(firstResult.Take(count.List));

嗯,我希望您不要对此感到太困惑。

Linq提供程序似乎有一些“意外”的行为。我可以用您描述的行数重现这个问题,并且在尝试使用子查询修复它时也遇到了一些问题

如果我正确理解您的意图,您基本上希望有一个记录符合特定标准的供应商的分页列表。 在这种情况下,我建议使用子查询。但是,我尝试使用
Query()
方法实现子查询,但没有成功。因此,我尝试了
QueryOver()
方法,该方法工作得非常完美。在您的情况下,所需的查询如下所示:

Provider pAlias = null;
var query = session.QueryOver<Provider>(() => pAlias).WithSubquery
                        .WhereExists(QueryOver.Of<Record>()
                            .Where(predicate)
                            .And(r => r.Provider.Id == pAlias.Id)
                            .Select(r => r.Provider));

var rowCount = query.RowCount();

var pageOfItems = query.Skip(firstResult).Take(count).List<Provider>();
这里的问题是您有多个查询,因此有多个到db的往返。当您检查生成的sql时,会出现另一个问题。子查询
idList.Contains(p.Id)
将生成一个包含大量参数的in子句

如您所见,这些是NHibernate.Linq查询。这是因为新的QueryOver功能不是真正的Linq提供程序,并且不能很好地与动态Linq表达式配合使用

您可以通过使用
分离的QueryOvers
来绕过该限制。这里的缺点是您必须以某种方式修改
ParseQueryExpression()
,例如:

private QueryOver<Record> ParseQueryExpression(string Query)
{
    Record rAlias = null;
    var detachedQueryOver = QueryOver.Of<Record>(() => rAlias)
                .JoinQueryOver(r => r.Provider)
                .Where(() => rAlias.Provider.TaxID == "000000000")
                .Select(x => x.Id);
    // modify your method to match the return value
    return detachedQueryOver;
}

// in your main method
var detachedQueryOver = QueryOver.Of<Record>()
    .WithSubquery
    .WhereProperty(r => r.Id
    .In(this.ParseQueryExpression(...));

var queryOverList = session.QueryOver<Provider>()
    .WithSubquery
    .WhereProperty(x => x.Id)
    .In(detachedQueryOver.Select(r => r.Provider.Id));

int rowCount = queryOverList.RowCount();
var pageOfItems = queryOverList.Skip(firstResult).Take(count).List();
private QueryOver ParseQueryExpression(字符串查询)
{
记录为空;
var detachedQueryOver=QueryOver.Of(()=>rAlias)
.JoinQueryOver(r=>r.Provider)
.Where(()=>rAlias.Provider.TaxID==“000000000”)
.选择(x=>x.Id);
//修改方法以匹配返回值
返回分离查询器;
}
//在你的主要方法中
分离变量