Linq .GroupBy .Where。选择新的分组项目

时间:2017-03-10 13:47:28

标签: c# linq grouping projection

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

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

这是我的更新日志模型:

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&gt;最新修订

  2. Group By EntityId

  3. 不要带回实体具有ChangeType.Created和ChangeType.Deleted的任何组

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

  5. 如果有倍数,则仅返回最后一条ChangeType.Updated记录

  6. 如果该组包含ChangeType.Created和ChangeType.Updated,则只返回最后一个ChangeType.Updated,但将其更改为ChangeType.Created

  7. 这是我已经走了多远,但我不知道如何'带回一部分人':

            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.如何将x转换为仅包含ChangeType.Deleted的记录/值的新组,如果组本身不包含ChangeType.Created但可能包含ChangeType.Updated?

1 个答案:

答案 0 :(得分:1)

我做过的一些假设:

  1. srcSet是一组ChangeLogObject项。
  2. 根据您列表中的要求5,启用&#34; last&#34; item,您应该能够按一些值对列表进行排序。为此,我引入了ChangeDate字段,该字段是更改发生的日期。
  3. 在我的示例中,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中,以确保在数据库而不是内存中评估这些查询。
    2. 这应该足以让您开始将需求的每个部分分解为查询。