使用QueryOver加入FirstOrDefault

时间:2014-09-08 14:09:00

标签: 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; }
}

变量是&#34;重要的&#34; class,除其他外,有一个IList of&#34; ValorVariable&#34; s,它有一个值(&#34; Valor&#34;)和一个值日期(&#34; FechaValor&#34;) 。 &#34; VariableLigera&#34;就像一个&#34;光&#34;变量类,具有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>();

这就是问题所在。我想加入物业&#34; Valor&#34;变量LigeraAlias是VariableLigera的Valores列表的FirstOrDefault值。但是它抛出了一个例外,&#34;函数FirstOrDefault无法识别&#34;。

所以我尝试了另一件事,为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>();

使用此查询,我得到结果,但我得到每个变量多次(ValoresVariables列表中每个值一个重复)。例如,如果一个变量有3个值,则查询将返回3&#34; VariablesLigera&#34; s,其中包含3个值,但我只想要一个具有它的第一个值的列表。

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

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

2 个答案:

答案 0 :(得分: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>();

这将生成如下所示的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

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

为此,您可以在子查询中添加OrderBy

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

除了ORDER BY子句之外,哪个会产生类似的SQL。

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

答案 1 :(得分:2)

如果我了解您,VariableLigera正在作为实体Variable的DTO运行。您需要Variable的大部分属性和Valores集合的一个值。   除非您想要集合的任何值,否则您需要指定选择第一个值的条件。由于VariableValor具有日期属性,因此我们使用它来选择最新值。

这是用子查询来做这件事。

未经测试的代码(它编译,但我没有执行它:

Variable variableAlias = null;
VariableLigera variableLigeraAlias = null;
ValorVariable valorVariableAlias = null;
var result = _session
        .QueryOver(() => variableAlias).Where(x => x.Eliminado == false)
        .JoinAlias(() => variableAlias.Valores, () => valorVariableAlias)
        .SelectList(projections => projections
            .Select(() => variableAlias.Id).WithAlias(() => variableLigeraAlias.Id),
            .Select(() => variableAlias.Nombre).WithAlias(() => variableLigeraAlias.Nombre),
            .Select(() => variableAlias.Descripcion).WithAlias(() => variableLigeraAlias.Descripcion),
            .Select(() => variableAlias.Temporal).WithAlias(() => variableLigeraAlias.Temporal),

            .SelectSubQuery(QueryOver.Of<ValorVariable>( () => valorVariableAlias)
                                .Where(vv => vv.IdVariable == variableAlias.Id)
            .Select(Projections.Max<ValorVariable>(vv => vv.FechaValor)))
                                .Select(Projections.Property(() => valorVariableAlias.Valor))
           .WithAlias(() => variableLigeraAlias.Valor)
        )
        .TransformUsing(Transformers.AliasToBean<VariableLigera>())
        .List<VariableLigera>();