NHIbernate 3.0 - QueryOver,使用相同的投影与distinct和order抛出sql错误

时间:2011-12-05 13:03:41

标签: nhibernate distinct queryover

我有一个生成sql错误的NHibernate QueryOver:如果指定了SELECT DISTINCT,则ORDER BY项必须出现在选择列表中

问题是由我用来选择的sql投影引起的,在哪里和顺序。因为投影本身使用sql函数,所以它有参数(常量:空格)。

当使用分配给变量的投影时,NH将此变量的每次使用唯一地转换为sql,这意味着每个都获得自己的新sql参数。因此Sql认为这些陈述是不同的。我试图使用别名进行投影无效,但似乎无法使用QueryOver执行此操作。

除了转回Criteria API之外,其他想法都会丢失。

这是简化的QueryIOver代码:

  var projection = ContactOrCompanyName();
  return Session.QueryOver<Contact>()
    .Select(
      Projections.Distinct(
        Projections.ProjectionList()
          .Add(Projections.Property<Contact>(x => x.Id).As("ContactId"))
          .Add(projection)
        )
    )
    .TransformUsing(Transformers.AliasToBean<ContactDto>())
    .OrderBy(projection).Asc;

private IProjection ContactOrCompanyName
    {
      get
      {
        return Projections.SqlFunction(
          "coalesce",
          NHibernateUtil.String,
          Projections.Property<Contact>(c => c.CompanyName),
          Projections.SqlFunction(
            "concat",
            NHibernateUtil.String,
            Projections.Property<Contact>(c => c.FirstName),
            Projections.Constant(" "),
            Projections.Property<Contact>(c => c.LastName)
          )
        );
      }
    }

导致以下sql:

SELECT distinct   
this_.CONTACT_ID as y0_, 
coalesce(this_.COMPANY_NM, (this_.FIRST_NM+@p0+this_.LAST_NM)) as y1_ 
FROM dbo.ADD_CONTACT this_ 
ORDER BY coalesce(this_.COMPANY_NM, (this_.FIRST_NM+@p1+this_.LAST_NM)) asc

Criteria API似乎支持通过此示例进行的别名重用:

IList results = session.CreateCriteria(typeof(DomesticCat), "cat")
    .CreateAlias("kittens", "kit")
    .SetProjection( Projections.ProjectionList()
        .Add( Projections.Property("cat.Name"), "catName" )
        .Add( Projections.Property("kit.Name"), "kitName" )
    )
    .AddOrder( Order.Asc("catName") )
    .AddOrder( Order.Asc("kitName") )
    .List();

那么QueryOver中的位置是什么?

1 个答案:

答案 0 :(得分:3)

这有点hacky,但它确实有效。通过创建两个用于比较的投影和一个用于选择的投影,我们可以通过投影不包含在选择列表中来阻止SQL抱怨订单:

      CompanyDirectorDto dtoAlias = null;
      var contactsQuery = Session.QueryOver<Contact>()
        .Select(
          Projections.Distinct(
            Projections.ProjectionList()
              .Add(Projections.Property<Contact>(x => x.Id).WithAlias(() => dtoAlias.ContactId))
              .Add(ContactOrCompanyNameComparer)
              .Add(ContactOrCompanyNameSelector.WithAlias(() => dtoAlias.ContactDisplayName))
            )
        )
        .TransformUsing(Transformers.AliasToBean<CompanyDirectorDto>())
        .OrderBy(ContactOrCompanyNameComparer).Asc;

private IProjection ContactOrCompanyNameSelector
{
  get
  {
    return Projections.SqlFunction(
      "coalesce",
      NHibernateUtil.String,
      Projections.Property<Contact>(c => c.CompanyName),
      Projections.SqlFunction(
        "concat",
        NHibernateUtil.String,
        Projections.Property<Contact>(c => c.FirstName),
        Projections.Constant(" "),
        Projections.Property<Contact>(c => c.LastName)
      )
    );
  }
}

private IProjection ContactOrCompanyNameComparer
{
  get
  {
    return Projections.SqlFunction(
      "coalesce",
      NHibernateUtil.String,
      Projections.Property<Contact>(c => c.CompanyName),
      Projections.SqlFunction(
        "concat",
        NHibernateUtil.String,
        Projections.Property<Contact>(c => c.FirstName),
        Projections.Property<Contact>(c => c.LastName)
      )
    );
  }
}