Linq .GroupBy .Where .Select 新建分组项

Linq .GroupBy .Where .Select new Grouping item

我正在尝试构建一个更改日志查询,以提取自上次修订以来的日志。

我现在需要根据一些规则合并日志。

这是我的更新日志模型:

public abstract class ChangeLogObject : TrackedObject {
    /// <summary>
    /// Type of transaction that occured
    /// </summary>
    public ChangeType ChangeType { get; set; }

    /// <summary>
    /// Original Id of the stateful object this changelog was created against
    /// </summary>
    public long EntityId { get; set; }

}
public enum ChangeType {
    Created,
    Updated,
    Deleted
}

所以我需要遵循的步骤是:

  1. 获取 Id > 上次修订

  2. 按 EntityId 分组

  3. 不要带回实体有 ChangeType.Created 和 ChangeType.Deleted

  4. 的任何组
  5. 如果有 ChangeType.Deleted 但没有 ChangeType.Created,则不要带回 ChangeType.Updated 记录(从组中删除更新的记录)

  6. 如果有多个

  7. 只带回最后ChangeType.Updated条记录
  8. 如果该组包含 ChangeType.Created 和 ChangeType.Updated 则只带回最后一个 ChangeType.Updated 但将其更改为 ChangeType.Created

这是我已经达到的程度,但我不知道如何'bring back part of a group':

        var newChanges = 
            // 1. Get where greater than last revision
            srcSet.Where(x => x.Id > lastRevision)

            // 2. Group by EntityId
            .GroupBy(x => x.EntityId)

            // 3. Don't bring back created and deleted (it's come and gone)
            .Where(x => !(x.Any(y => y.ChangeType == ChangeType.Created) &&
                        x.Any(y => y.ChangeType == ChangeType.Deleted)))

            // 4. Only bring back Deleted if no created (don't edit something you're about to delete)
            .Where(x => (!x.Any(y => y.ChangeType == ChangeType.Created) && 
                        x.Any(y => y.ChangeType == ChangeType.Deleted)))
                        //create new grouping, but only use the delete record????

            //convert back to individual change logs
            .Select(x => x.ToList()
            //maybe order by ID?
            .OrderBy(y => y.Id));

对于 4. 如果组本身不包含 ChangeType.Created 但可能包含 [,我如何将 x 变成一个只有 records/values 的新组 ChangeType.Deleted =55=]?

我做出的一些假设:

  1. srcSet 是一组 ChangeLogObject 项。
  2. 根据列表中的要求 5,要启用 "last" 项目的检索,您应该能够按某个值对列表进行排序。为此,我引入了一个 ChangeDate 字段,它是更改发生的日期。

在我的示例中,TrackedObject 如下所示:

public abstract class TrackedObject
{
        public int Id { get; set; }
        public DateTime ChangeDate { get; set; }
}

我把查询的每一步都分解了,并按要求编号:

var lastRevision = 2;
var srcSet = new List<ChangeLogObject>();

// 1. & 2.
var entityGroupsByEntityId = srcSet
    .Where(m => m.Id > lastRevision) // greater than last revision
    //.OrderByDescending(m => m.ChangeDate)
    .GroupBy(m => m.EntityId)
    .Select(group => new
    {
        EntityId = group.Key,
        ChangeCount = group.Count(),
        Changes = group.ToList().OrderByDescending(m => m.ChangeDate)
    });

//  3.
var entityGroupsWithNoDeleteAndCreate = entityGroupsByEntityId
    .Where(group => !(group.Changes.Any(m => m.ChangeType == ChangeType.Created) 
    && group.Changes.Any(m => m.ChangeType == ChangeType.Deleted))); // it doesn't contain both creates and deletes

// 4. 
var entityGroupsWithoutDeletedAndNoCreate = entityGroupsWithNoDeleteAndCreate
    .Where(group => !(group.Changes.Any(m => m.ChangeType == ChangeType.Deleted) 
    && (group.Changes.Count(m => m.ChangeType == ChangeType.Created) < 1))); // it doesn't contain a delete without a create

// 5. 
var entityGroupsWithUpdatedButNoCreated = entityGroupsWithoutDeletedAndNoCreate
    .Where(group => group.Changes.Any(m => m.ChangeType == ChangeType.Updated) 
    && !group.Changes.Any(m => m.ChangeType == ChangeType.Created)) // it updated but not created
    .Select(group => new ChangeLogObject
    {
        EntityId = group.EntityId,
        ChangeDate = group.Changes.First().ChangeDate,
        ChangeType = group.Changes.First().ChangeType // don't change the type
    });

// 6.
var entityGroupsWithCreatedAndUpdatedOnly = entityGroupsWithoutDeletedAndNoCreate
    .Where(group => group.Changes.Any(m => m.ChangeType == ChangeType.Created) 
    && group.Changes.Any(m => m.ChangeType == ChangeType.Updated)) // it contains both created and updated
    .Select(group => new ChangeLogObject
    {
        EntityId = group.EntityId,
        ChangeDate = group.Changes.First(m => m.ChangeType == ChangeType.Updated).ChangeDate, // bring back the first updated record
        ChangeType = ChangeType.Created // change the type to created
    });

// Join the 2 sets of Updated and Created changes
var finalResult = entityGroupsWithUpdatedButNoCreated.Union(entityGroupsWithCreatedAndUpdatedOnly).ToList();

警告:

  1. linq 语句未针对性能进行优化,因为它们未包含在 IQueryable 中以确保这些查询是在数据库而不是内存中计算的。

这应该足以让您开始将需求的每个部分分解为一个查询。