使用QueryOver加入FirstOrDefault

xec*_*ons 1 c# nhibernate

首先,我是NHibernate的新手.我在许多页面中搜索了QueryOver的内容,但对我来说最有利可图的页面是http://nhibernate.info/blog/2009/12/17/queryover-in-nh-3-0.html.多亏了它,我想到了一个"半解决方案",但我需要这个解决方案更好.

我有这些课程:

public class Variable
{
    public virtual int Id {get; set; }
    public virtual string Nombre { get; set; }
    public virtual string Descripcion { get; set; }
    public virtual IList<ValorVariable> Valores { get; set; }
    public virtual bool Temporal { get; set; }
    public virtual bool Eliminado{ get; set; }
}

public class ValorVariable
{
    public virtual int Id {get; set; }
    public virtual int IdVariable { get; set; }
    public virtual Variable Variable { get; set; }
    public virtual DateTime FechaValor { get; set; }
    public virtual decimal Valor { get; set; }
}

public class VariableLigera
{
    public virtual int Id {get; set; }
    public string Nombre { get; set; }
    public string Descripcion { get; set; }
    public bool Temporal { get; set; }
    public Decimal Valor { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

变量是"重要"类,除其他外,它具有IList"ValorVariable",具有值("Valor")和值日期("FechaValor")."VariableLigera"就像一个"轻"类变量,具有Variable的一些属性和ValorVariable List的一个值.我希望这很清楚.

要填充VariableLigera,我正在尝试执行QueryOver.我想做这样的事情:

Variable variableAlias = null;
VariableLigera variableLigeraAlias = null;

var result = _session
            .QueryOver(() => variableAlias ).Where(x => x.Eliminado == false)
            .Select(
                Projections.Property(() => variableAlias .Id).WithAlias(() => variableLigeraAlias .Id),
                Projections.Property(() => variableAlias .Nombre).WithAlias(() => variableLigeraAlias .Nombre),
                Projections.Property(() => variableAlias .Descripcion).WithAlias(() => variableLigeraAlias .Descripcion),
                Projections.Property(() => variableAlias .Temporal).WithAlias(() => variableLigeraAlias .Temporal),
                Projections.Property(() => variableAlias .Valores.FirstOrDefault().Valor).WithAlias(() => variableLigeraAlias .Valor)
            )
            .TransformUsing(Transformers.AliasToBean<VariableLigera>())
            .List<VariableLigera>();
Run Code Online (Sandbox Code Playgroud)

这就是问题所在.我想将变量LigeraAlias的属性"Valor"放入变量列表的Valores列表的FirstOrDefault值.但它抛出一个异常,"函数FirstOrDefault无法识别".

所以我尝试了另一件事,为ValorVariable创建一个别名并将其加入查询.像这样:

Variable variableAlias = null;
VariableLigera variableLigeraAlias = null;
ValorVariable valorVariableAlias = null;
var result = _session
            .QueryOver(() => variableAlias).Where(x => x.Eliminado == false)
            .JoinAlias(() => variableAlias.Valores, () => valorVariableAlias)
            .Select(
                Projections.Property(() => variableAlias.Id).WithAlias(() => variableLigeraAlias.Id),
                Projections.Property(() => variableAlias.Nombre).WithAlias(() => variableLigeraAlias.Nombre),
                Projections.Property(() => variableAlias.Descripcion).WithAlias(() => variableLigeraAlias.Descripcion),
                Projections.Property(() => variableAlias.Temporal).WithAlias(() => variableLigeraAlias.Temporal),
                Projections.Property(() => valorVariableAlias.Valor).WithAlias(() => variableLigeraAlias.Valor)
            )
            .TransformUsing(Transformers.AliasToBean<VariableLigera>())
            .List<VariableLigera>();
Run Code Online (Sandbox Code Playgroud)

通过这个查询,我得到了结果,但是我得到了每个变量很多次(ValoresVariables列表中每个值有一个重复).例如,如果一个变量有3个值,查询将返回带有3个值的3个"VariablesLigera",但我只想要一个带有它的列表的第一个值.

简而言之,为了清楚起见,我希望每个变量都有一个结果,其值为("Valor")列表中的firstordefault值.是否可以使用QueryOver?非常感谢.

PS:因为英语不是我的母语,也许有些事情不太容易理解.如果有任何疑问,请随时询问.再次感谢.

And*_*ker 5

使用QueryOver查询时要记住的事情是它们最终变成了SQL.考虑到这一点,无论何时访问嵌套属性,都需要考虑连接或子查询来完成工作.

在这种情况下,您需要使用子查询来获取第一个ValorVariable.Valor:

Variable variableAlias = null;
VariableLigera variableLigeraAlias = null;

var result = _session
    .QueryOver(() => variableAlias).Where(x => x.Eliminado == false)
    .Select(
        Projections.Property(() => variableAlias.Id).WithAlias(() => variableLigeraAlias.Id),
        Projections.Property(() => variableAlias.Nombre).WithAlias(() => variableLigeraAlias.Nombre),
        Projections.Property(() => variableAlias.Descripcion).WithAlias(() => variableLigeraAlias.Descripcion),
        Projections.Property(() => variableAlias.Temporal).WithAlias(() => variableLigeraAlias.Temporal),
        Projections.Subquery(
            QueryOver.Of<ValorVariable>()
                .Where(vv => vv.Variable.Id == variableAlias.Id)
                .Select(vv => vv.Valor)
                .Take(1)
        ).WithAlias(() => variableLigeraAlias.Valor)
    )
    .TransformUsing(Transformers.AliasToBean<VariableLigera>())
    .List<VariableLigera>();
Run Code Online (Sandbox Code Playgroud)

这将生成如下所示的SQL:

SELECT 
    /*Variable properties */
    (       
        SELECT 
            TOP(1) this_0_.Valor AS y0_ 
        FROM 
            ValorVariable this_0_ 
        WHERE 
            this_0_.VariableID = this_.ID
    ) AS y1_ 
FROM 
    Variable this_ 
WHERE 
    this_.Eliminado = 0
Run Code Online (Sandbox Code Playgroud)

现在,正如@Carl指出的那样,你可能应该通过某种方式来命令你的子查询结果,以便取"第一条记录"实际意味着什么.

为此,您可以OrderBy向子查询添加:

QueryOver.Of<ValorVariable>()
    .Where(vv => vv.Variable.Id == variableAlias.Id)
    .Select(vv => vv.Valor)
    .OrderBy(vv => vv.FechaValor).Desc
    .Take(1)
Run Code Online (Sandbox Code Playgroud)

哪个会产生类似的SQL,除了一个ORDER BY子句.

此外,由于您使用FirstOrDefault,这意味着该Valor财产可能存在null.如果是这种情况,您应该将您的VariableLigera.Valor属性更新为decimal?(可为空decimal).