如何使用NHibernate QueryOver重新创建这个复杂的SQL查询?

如何使用NHibernate QueryOver重新创建这个复杂的SQL查询?,nhibernate,subquery,queryover,Nhibernate,Subquery,Queryover,想象一下以下(简化的)数据库布局: 我们有很多“假期”记录,涉及到在某个日期去某个特定的住处等 我想从数据库中提取每间住宿的“最佳”假期(即最低价格),给出一组搜索条件(如持续时间、出发机场等) 将有多个记录具有相同的价格,因此我们需要按报价保存(降序),然后按出发日期升序进行选择 我可以编写SQL来完成这样的任务(我不是说这一定是最好的方法): 如您所见,这在另一个子查询中使用了一个子查询 然而,出于几个原因,我倾向于在NHibernate中实现同样的结果 理想情况下,这将通过QueryOv

想象一下以下(简化的)数据库布局:

我们有很多“假期”记录,涉及到在某个日期去某个特定的住处等

我想从数据库中提取每间住宿的“最佳”假期(即最低价格),给出一组搜索条件(如持续时间、出发机场等)

将有多个记录具有相同的价格,因此我们需要按报价保存(降序),然后按出发日期升序进行选择

我可以编写SQL来完成这样的任务(我不是说这一定是最好的方法):

如您所见,这在另一个子查询中使用了一个子查询

然而,出于几个原因,我倾向于在NHibernate中实现同样的结果

理想情况下,这将通过QueryOver完成——原因是我动态地建立了搜索条件,而使用QueryOver的fluent界面更容易实现。(我本来希望使用NHibernate Linq,但不幸的是它还不够成熟)

经过大量的努力(作为NHibernate的一个相对新手),我能够重新创建一个非常内部的查询,该查询获取所有住宿及其最低价格

public IEnumerable<HolidaySearchDataDto> CriteriaFindAccommodationFromPricesForOffers(IEnumerable<IHolidayFilter<PackageHoliday>> filters, int skip, int take, out bool hasMore)
    {
        IQueryOver<PackageHoliday, PackageHoliday> queryable = NHibernateSession.CurrentFor(NHibernateSession.DefaultFactoryKey).QueryOver<PackageHoliday>();

        queryable = queryable.Where(h => h.TradeNameId == website.TradeNameID);

        var accommodation = Null<Accommodation>();
        var accommodationUnit = Null<AccommodationUnit>();
        var dto = Null<HolidaySearchDataDto>();

        // Apply search criteria
        foreach (var filter in filters)
            queryable = filter.ApplyFilter(queryable, accommodationUnit, accommodation);

        var query1 = queryable

            .JoinQueryOver(h => h.AccommodationUnit, () => accommodationUnit)
            .JoinQueryOver(h => h.Accommodation, () => accommodation)
            .SelectList(hols => hols
                                    .SelectGroup(() => accommodation.Id).WithAlias(() => dto.AccommodationId)
                                    .SelectMin(h => h.Price).WithAlias(() => dto.Price)
            );

        var list = query1.OrderByAlias(() => dto.Price).Asc
            .Skip(skip).Take(take+1)
            .Cacheable().CacheMode(CacheMode.Normal).List<object[]>();

        // Cacheing doesn't work this way...
        /*.TransformUsing(Transformers.AliasToBean<HolidaySearchDataDto>())
        .Cacheable().CacheMode(CacheMode.Normal).List<HolidaySearchDataDto>();*/

        hasMore = list.Count() == take;

        var dtos = list.Take(take).Select(h => new HolidaySearchDataDto
                    {
                        AccommodationId = (string)h[0],
                        Price = (decimal)h[1],
                    });

        return dtos;
    }
公共IEnumerable标准FINDAComdationfromPrices提供(IEnumerable过滤器、int skip、int take、out bool hasMore)
{
IQueryOver queryable=NHibernateSession.CurrentFor(NHibernateSession.DefaultFactoryKey).QueryOver();
queryable=queryable.Where(h=>h.TradeNameId==website.TradeNameId);
var=Null();
var-onUnit=Null();
var dto=Null();
//应用搜索条件
foreach(过滤器中的var过滤器)
queryable=filter.ApplyFilter(queryable,调节单元,调节);
变量query1=可查询
.JoinQueryOver(h=>h.acbulationUnit,()=>acbulationUnit)
.JoinQueryOver(h=>h.住宿,()=>住宿)
.选择列表(hols=>hols
.SelectGroup(()=>accountability.Id)。带别名(()=>dto.accountability.Id)
.SelectMin(h=>h.Price)。带别名(()=>dto.Price)
);
var list=query1.OrderByAlias(()=>dto.Price).Asc
.Skip(Skip)。Take(Take+1)
.Cacheable().CacheMode(CacheMode.Normal).List();
//缓存不是这样工作的。。。
/*.TransformUsing(Transformers.AliasToBean())
.Cacheable().CacheMode(CacheMode.Normal).List()*/
hasMore=list.Count()=take;
var dtos=list.Take(Take).Select(h=>newholidaysearchdatadto
{
调节ID=(字符串)h[0],
价格=(十进制)h[1],
});
返回DTO;
}
所以我的问题是…

关于如何使用QueryOver或者必要时使用Criteria API实现我想要的东西,有什么想法吗

我宁愿不使用HQL,但如果有必要的话,我也愿意看看如何使用HQL(这会使建立搜索条件变得更困难(或更混乱)


如果使用NHibernate无法做到这一点,那么我可以使用SQL查询。在这种情况下,我的问题是SQL是否可以改进/优化?

我已经通过使用Criteria API实现了这种动态搜索标准。我遇到的问题是内部连接和外部连接的重复,尤其是与排序和分页有关,我不得不求助于使用2个查询,第一个查询用于限制,并将第一个查询的结果用作第二个creteria中的“in”子句。

恐怕不行。最后我把它分成了两个查询。是的,这就是我最后所做的。
public IEnumerable<HolidaySearchDataDto> CriteriaFindAccommodationFromPricesForOffers(IEnumerable<IHolidayFilter<PackageHoliday>> filters, int skip, int take, out bool hasMore)
    {
        IQueryOver<PackageHoliday, PackageHoliday> queryable = NHibernateSession.CurrentFor(NHibernateSession.DefaultFactoryKey).QueryOver<PackageHoliday>();

        queryable = queryable.Where(h => h.TradeNameId == website.TradeNameID);

        var accommodation = Null<Accommodation>();
        var accommodationUnit = Null<AccommodationUnit>();
        var dto = Null<HolidaySearchDataDto>();

        // Apply search criteria
        foreach (var filter in filters)
            queryable = filter.ApplyFilter(queryable, accommodationUnit, accommodation);

        var query1 = queryable

            .JoinQueryOver(h => h.AccommodationUnit, () => accommodationUnit)
            .JoinQueryOver(h => h.Accommodation, () => accommodation)
            .SelectList(hols => hols
                                    .SelectGroup(() => accommodation.Id).WithAlias(() => dto.AccommodationId)
                                    .SelectMin(h => h.Price).WithAlias(() => dto.Price)
            );

        var list = query1.OrderByAlias(() => dto.Price).Asc
            .Skip(skip).Take(take+1)
            .Cacheable().CacheMode(CacheMode.Normal).List<object[]>();

        // Cacheing doesn't work this way...
        /*.TransformUsing(Transformers.AliasToBean<HolidaySearchDataDto>())
        .Cacheable().CacheMode(CacheMode.Normal).List<HolidaySearchDataDto>();*/

        hasMore = list.Count() == take;

        var dtos = list.Take(take).Select(h => new HolidaySearchDataDto
                    {
                        AccommodationId = (string)h[0],
                        Price = (decimal)h[1],
                    });

        return dtos;
    }