linq 如何进一步过滤查询的.Include(.Where())并使其反映在Include()之外

68bkxrlz  于 2023-01-18  发布在  其他
关注(0)|答案(2)|浏览(172)

我知道我的问题有点混乱,因为我还没有找到一个更好的方式来问它,但我相信这不是一个很难解决的问题。
事情是这样的:
我应该在实体存储库中名为GetAllPlaces的方法中返回一个List<Place>
地点实体:

public Guid PlaceId { get; set; }
public string Name { get; set; }
public List<Hour> Hours { get; set; }

小时实体:

public Guid HourId { get; set; }
public Guid DayOfTheWeekId { get; set; }
public DayOfTheWeek DayOfTheWeek { get; set; }
public DateTime OpenHour { get; set; }
public DateTime CloseHour { get; set; }
public Guid PlaceId { get; set; }
public Place Place { get; set; }

每个Place都有一个List<Hour>属性,我尝试过滤这个小时列表,不返回对这个方法的调用者关闭的地点,到目前为止,我只过滤了这个地点所在时区的今天的小时:

public async Task<IReadOnlyList<Place>>
            GetAllPlacesAsync()
        {    
            var places = await context.Places
                .AsNoTracking()

                .Include(s => s.Hours
                    // here I'm filtering to get just today's Hour like explained previously
                    .Where(d => d.DayOfTheWeek.DayName
                        == TimeZoneInfo
                        .ConvertTime(DateTime.Now,
                            TimeZoneInfo
                            .FindSystemTimeZoneById(d.Place.Timezone.Name))
                            .DayOfWeek
                            .ToString()).FirstOrDefault())
                // a second .Where() would filter on the .Include()
                // or on the "places" List but not on its Hours.
                // How to further filter to do something like this:
                // if Place.Hour.Open <= timeNowInPlaceTimezone
                // && Place.Hour.Close >= timeNowInPlaceTimezone ? passToList : dontPassToList

                .Distinct()
                .ToListAsync();

            return places;
        }

你知道我如何过滤它,只得到地方的Hour是在开放和关闭时间之间的今天在其时区的地方?

c9qzyr3d

c9qzyr3d1#

首先澄清一下我的理解或您的数据:每个地点都有一个List<Hour>集合,其中每个Hour对象都有属性DayOfTheWeekOpenClose-类似于store hours sign上的行项目。此外,每个place都有一个关联的时区,在检查place.Hours集合之前,需要将当前(UTC)日期/时间转换为该时区。
我不认为.Include()是您在这里想要的,因为该函数是用于选择额外的数据,以便与结果一起加载,而不是用于过滤结果。
我的期望是您想要类似.Where(place => place.Hours.Any(hour => hours-matches-local-time)的东西。
但是,hours-matches-local-time部分可能包含复杂的计算,可能不必要地在.Any()内部对同一位置进行多次评估。为避免冗余计算,外部.Where()中的条件可被制成代码块,其中本地星期几和本地时间在每个位置计算一次,并被分配给变量,然后在内部.Any()内重复引用。
尝试以下内容:

var places = await context.Places
    .AsNoTracking()
    .Where(place => {
        var localDateTime = ...;
        var dayOfWeekInPlaceTimezone = ...;
        var timeNowInPlaceTimezone  = ...;
        bool isOpen = place.Hours
            .Any(hour =>
                hour.DayOfTheWeek == dayOfWeekInPlaceTimezone
                && hour.Open <= timeNowInPlaceTimezone
                && hour.Close > timeNowInPlaceTimezone
            );
        return isOpen;
    })
    //(not needed) .Distinct() 
    .ToListAsync();

以上使用 LINQ Method syntax。另一种方法是使用 LINQ Query syntax,它也可以使用let子句计算局部变量。This post提供了演示其中一些技术的答案。(也许在LINQ查询语法方面比我更有经验的人可以发布翻译。)
可能还有其他改进的机会,例如按时区对地点分组,并对每个组计算一次当地日期/时间。最终将使用.SelectMany()来处理每个组并使结果变平。结果可能如下所示:

var places = await context.Places
    .AsNoTracking()
    .GroupBy(place => place.Timezone)
    .SelectMany(tzGroup => {
        var timezone = tzGroup.Key;
        var localDateTime = ...;
        var dayOfWeekInPlaceTimezone = ...;
        var timeNowInPlaceTimezone = ...;
        return tzGroup.Where(place =>
            place.Hours.Any(hour =>
                hour.DayOfTheWeek == dayOfWeekInPlaceTimezone
                && hour.Open <= timeNowInPlaceTimezone
                && hour.Close > timeNowInPlaceTimezone
            )
        );
    })
    //(not needed) .Distinct() 
    .ToListAsync();
y4ekin9u

y4ekin9u2#

不确定您的开放/关闭时间条件,但查询应如下所示:

public async Task<IReadOnlyList<Place>>  GetAllPlacesAsync()
{    
    var dayOfWeek = TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo .FindSystemTimeZoneById(d.Place.Timezone.Name))
        .DayOfWeek
        .ToString();

    var places = await context.Places
        .AsNoTracking()
        .Where(p => p.Hours.Any(h =>
                h.DayOfTheWeek.DayName == dayOfWeek 
                && h.Open <= timeNowInPlaceTimezone
                && h.Close >= timeNowInPlaceTimezone
            )
        )
        .ToListAsync();

    return places;
}

相关问题