2017-03-10 6 views
1

私は最後のリビジョン以降にログを取得する変更ログクエリを作成しようとしています。Linq。GroupBy .Where。新しいグループ化アイテムを選択してください。

いくつかのルールに基づいてログを統合する必要があります。

ここに私の変更履歴モデルです:

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 
} 

だから私はに必要な手順は次のとおりです。どこID>最後のリビジョン

  • グループによって実体識別子

    1. ゲット

      エンティティがChangeTypeを持つグループを戻しません.CreatedとChangeType.Deleted

    2. ChangeType.Deletedが、無ChangeType.CreatedがあるかどうChangeType.Updatedレコードを戻すしないでください

    3. (グループからの更新されたレコードを削除します)のみ、最後の変更タイプを戻します.Updated記録倍数

    4. がある場合、グループはChangeType.CreatedとChangeType.Updatedが含まれている場合は、最後のChangeType.Updatedを取り戻すが、ChangeType.Created

    に変更するだけ210

    これは私が持っているどのくらいであるが、私は「グループの一部持ち帰る」するのか分からない。私はレコードのみを使用して新しいグループにXを有効にするにはどうすればよい4.について

     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)); 
    

    を/値はChangeType.Deletedです。グループ自体にChangeType.Createdが含まれていない場合は削除されますが、ChangeType.Updatedが含まれている可能性があります。私が作った

  • +1

    この大きなクエリを中間の結果変数を持つ小さなものに分割する方が簡単かもしれません – slawekwin

    +0

    どういう意味ですか?すべての部品を個別に選択し、それらを結合しますか?またはそれらをメモリにプルしてから操作しますか? – Smithy

    答えて

    1

    いくつかの仮定:

    1. srcSetChangeLogObjectアイテムのセットです。
    2. リストの要件5によると、「最後の」アイテムの取得を有効にするには、リストをある値でソートできる必要があります。このために、変更が発生した日付であるChangeDateフィールドを導入しました。私のサンプルで

    TrackedObjectは以下のようになります。

    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(); 
    

    警告:私は要件に応じて、クエリの各ステップを分解し、それらの番号を付けました

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

    1. linq文が最適化されていないこれらのクエリはIn-Memoryではなくデータベース上で評価されるようにIQueryableでラップされていないため、パフォーマンスが低下します。

    これは、要件の各部分をクエリに分解するのに十分なはずです。

    +0

    あなたは真に仲間を超えています。ありがとうございます。私はこの作品を作ることができると確信しています(バットからすぐに出ない場合)が、EntityFrameworkCoreのフレームワークの問題があると思いますので、私が編集でアップロードした画像について別の質問をします:) – Smithy

    +0

    私はToList()をグループの前に置くことで作業していますが、理想的ではありませんが、コアがもう少し複雑になるまでは効率の点で違いはありません。私はジェネリックスも使用していますので、 '(DomainChangeLog)(object)group.Changes.First()'を使用してキャストしなければなりませんでした。 'srcSet = db.Set ();ここで、DomainChangeLog:ChangeLogObject' – Smithy

    +1

    これは、これが舞台裏でリレーショナルデータストアを使用している場合は、SQLビューとして作成することをお勧めします。 :) – Terence

    関連する問題