我有一个生成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中的位置是什么?
答案 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)
)
);
}
}