【问题标题】:Linq: Group by date intervals (5, 10, 15, etc.. years)Linq:按日期间隔分组(5、10、15 等.. 年)
【发布时间】:2026-01-27 05:35:01
【问题描述】:

我有一个 MVC/ASP.NET Web 应用程序,用于跟踪员工信息,例如 StartDate,它是一个 DateTime 数据类型,表示特定员工在公司开始工作的时间。

我们还为在公司工作的员工颁发奖励,并希望按在公司工作 5、10、15、20、25、30 年以上的时间分组。

我如何编写一个 Linq 语句,该语句仅按在公司的时间已达到将收到“在公司的时间”奖?感谢您的帮助。

【问题讨论】:

  • 您需要展示您尝试过的内容,提供一些示例代码。你有没有寻找答案,因为我很确定这已经被问过了?
  • @DavidG - 我一直在环顾四周,但没有找到可行的解决方案。我发现的那些要么专门用于 SQL(并且无法转换为 linq),要么不适用于之前设置的静态间隔。我拥有的唯一示例代码是查询数据上下文以获取公司内活跃的人员 (!EndDate.HasValue),并按 StartDate 分组。我以前做过很多分组,只是以前没有这样的集合数据。
  • @Nkosi - 抱歉,我的情况略有不同,因为我只想对那些特别适合该年份间隔的人进行独家查询。不是所有人。
  • 分组后过滤
  • 第一个缩小分组范围的过滤器是那些开始日期早于 5 年的人,然后按您的时间间隔开始分组

标签: c# asp.net sql-server linq


【解决方案1】:

这个解决方案做得很好!使用您的解决方案,我采用了您的主要逻辑并进行了一些更改,这些更改足以使我认为其他人可能会从中受益。凭借您在帮助方面的耐心以及您对我试图完成的工作的专业知识,绝对无法做到这一点。

请参阅下面的更改...

控制器

public ActionResult LengthOfService()
{
    ViewBag.Title = "Length of Service Awards Report";
    var people = db.People.ToList().Where(p => !p.EndDate.HasValue);
    var now = DateTime.Today;
    var period = 5;
    var maxPeriod = 30;
    var query = (from p in people
                let interval = DateTime.MinValue.AddDays((now - p.LengthOfService).TotalDays).Year / period
                where interval > 0
                group p by Math.Min(interval * period, maxPeriod) into x
                orderby x.Key
                select new AwardInfo
                {
                    Years = x.Key,
                    People = x
                }).ToList(); 
    return View(query);
}

查看模型

public class AwardInfo
{
    public int Years { get; set; }
    public IEnumerable<People> People { get; set; }
}

查看

@model List<CPR.Models.AwardInfo>

<h2>@ViewBag.Title</h2>

<table class="table table-responsive table-hover">
    <thead>
        <tr>

            <th class="col-sm-1">Service Award</th>
            <th class="col-sm-2">Name</th>
            <th class="col-sm-2">Date of Service</th>
            <th class="col-sm-2">Company</th>
        </tr>
    </thead>
    @foreach (var item in Model)
    {
        <tbody>
            <tr>
                <th class="panel-bg" colspan="5">@item.Years years</th>
            </tr>
        </tbody>
        <tbody>
            @foreach (var person in item.People)
            {
                <tr>
                    <td class="col-sm-1"></td>
                    <td class="col-sm-2">@person.LastName, @person.FirstName</td>
                    <td class="col-sm-2">@person.LengthOfService.ToShortDateString()</td>
                    <td class="col-sm-2">@person.Companies.Name</td>
                </tr>
            }
        </tbody>
    }
</table>

【讨论】:

    【解决方案2】:

    查看以下用于演示如何按周期/间隔分组的单元测试。

    [TestClass]
    public class GroupByDateIntervalsTests {
        [TestMethod]
        public void Group_By_5_year_Intervals_Max_30() {
            var employees = GenerateRandomDates(DateTime.Now, 5, 40, 50).Select((d, i) => new { id = i, StartDate = d });
    
            var now = DateTime.Today;
            var period = 5;
            var maxPeriod = 30;
            var groups = from employee in employees
                         let interval = DateTime.MinValue.AddDays((now - employee.StartDate).TotalDays).Year / period
                         group employee by Math.Min(interval * period, maxPeriod) into g
                         orderby g.Key
                         select new {
                             period = g.Key,
                             employees = g.Select(e => e.id).ToArray()
                         };
    
            var result = groups.ToList();
        }
    
        [TestMethod]
        public void Group_By_Random_Interval_Max_30() {
            var employees = GenerateRandomDates(DateTime.Now, 5, 40, 50).Select((d, i) => new { id = i, StartDate = d });
    
            var now = DateTime.Today;
            var periods = new[] { 5, 10, 20, 30 };
            var groups = employees
                .GroupBy(employee => {
                    var period = DateTime.MinValue.AddDays((now - employee.StartDate).TotalDays).Year;
                    var interval = periods.Where(p => (period / p) > 0).Max();
                    return Math.Min(interval, periods.Max());
                })
                .Select(g => new {
                    period = g.Key,
                    employees = g.Select(e => e.id).ToArray()
                });
    
            var result = groups.ToList();
        }
    
        public List<DateTime> GenerateRandomDates(DateTime rootDate, int minAgeAtRootDate, int maxAgeAtRootDate, int count) {
            Contract.Assert(minAgeAtRootDate <= maxAgeAtRootDate, "invalid age range. Minimum age cannot be higher than maximum age");
            var minDate = rootDate.Date.AddYears(-maxAgeAtRootDate);
            var maxDate = rootDate.Date.AddYears(-minAgeAtRootDate);
            var range = (maxDate - minDate).Days;
            if (range == 0) {
                range = 364;
            }
            var random = new Random();
            var dates = Enumerable
                .Range(1, count)
                .Select(i => minDate.AddDays(random.Next(range)))
                .ToList();
            return dates;
        }
    }
    

    【讨论】:

      最近更新 更多