<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          EFCore 5 新特性 SaveChangesInterceptor

          共 8189字,需瀏覽 17分鐘

           ·

          2020-11-19 15:44

          EFCore 5 新特性 SaveChangesInterceptor

          Intro

          之前 EF Core 5 還沒正式發(fā)布的時(shí)候有發(fā)布過一篇關(guān)于 SaveChangesEvents 的文章,有需要看可以移步到 efcore 新特性 SaveChanges Events,在后面的版本中又加入了 Interceptor 的支持,可以更方便的實(shí)現(xiàn) SaveChanges 事件的復(fù)用, 今天主要介紹一下通過 SaveChangesInterceptor 來實(shí)現(xiàn)日志審計(jì)

          SaveChangesInterceptor

          源碼實(shí)現(xiàn):

          public?interface?ISaveChangesInterceptor?:?IInterceptor
          {
          ????///?
          ????///?????Called?at?the?start?of?.
          ????///?

          ????///??Contextual?information?about?the??being?used.?
          ????///?
          ????///?????Represents?the?current?result?if?one?exists.
          ????///?????This?value?will?have??set?to??if?some?previous
          ????///?????interceptor?suppressed?execution?by?calling?.
          ????///?????This?value?is?typically?used?as?the?return?value?for?the?implementation?of?this?method.
          ????///?
          ????///?
          ????///?????If??is?false,?the?EF?will?continue?as?normal.
          ????///?????If??is?true,?then?EF?will?suppress?the?operation?it
          ????///?????was?about?to?perform?and?use??instead.
          ????///?????A?normal?implementation?of?this?method?for?any?interceptor?that?is?not?attempting?to?change?the?result
          ????///?????is?to?return?the??value?passed?in.
          ????///?
          ????InterceptionResult<int>?SavingChanges(
          ????????[NotNull]?DbContextEventData?eventData,
          ????????InterceptionResult<int>?result
          )
          ;

          ????///?
          ????///?????
          ????///?????????Called?at?the?end?of?.
          ????///?????
          ????///?????
          ????///?????????This?method?is?still?called?if?an?interceptor?suppressed?creation?of?a?command?in?.
          ????///?????????In?this?case,??is?the?result?returned?by?.
          ????///?????
          ????///?

          ????///??Contextual?information?about?the??being?used.?
          ????///?
          ????///?????The?result?of?the?call?to?.
          ????///?????This?value?is?typically?used?as?the?return?value?for?the?implementation?of?this?method.
          ????///?
          ????///?
          ????///?????The?result?that?EF?will?use.
          ????///?????A?normal?implementation?of?this?method?for?any?interceptor?that?is?not?attempting?to?change?the?result
          ????///?????is?to?return?the??value?passed?in.
          ????///?
          ????int?SavedChanges(
          ????????[NotNull]?SaveChangesCompletedEventData?eventData,
          ????????int?result
          )
          ;

          ????///?
          ????///?????Called?when?an?exception?has?been?thrown?in?.
          ????///?

          ????///??Contextual?information?about?the?failure.?
          ????void?SaveChangesFailed(
          ????????[NotNull]?DbContextErrorEventData?eventData
          )
          ;

          ????///?
          ????///?????Called?at?the?start?of?.
          ????///?

          ????///??Contextual?information?about?the??being?used.?
          ????///?
          ????///?????Represents?the?current?result?if?one?exists.
          ????///?????This?value?will?have??set?to??if?some?previous
          ????///?????interceptor?suppressed?execution?by?calling?.
          ????///?????This?value?is?typically?used?as?the?return?value?for?the?implementation?of?this?method.
          ????///?
          ????///??The?cancellation?token.?
          ????///?
          ????///?????If??is?false,?the?EF?will?continue?as?normal.
          ????///?????If??is?true,?then?EF?will?suppress?the?operation?it
          ????///?????was?about?to?perform?and?use??instead.
          ????///?????A?normal?implementation?of?this?method?for?any?interceptor?that?is?not?attempting?to?change?the?result
          ????///?????is?to?return?the??value?passed?in.
          ????///?
          ????ValueTaskint>>?SavingChangesAsync(
          ????????[NotNull]?DbContextEventData?eventData,
          ????????InterceptionResult<int>?result,
          ????????CancellationToken?cancellationToken?=?default);

          ????///?
          ????///?????
          ????///?????????Called?at?the?end?of?.
          ????///?????
          ????///?????
          ????///?????????This?method?is?still?called?if?an?interceptor?suppressed?creation?of?a?command?in?.
          ????///?????????In?this?case,??is?the?result?returned?by?.
          ????///?????
          ????///?

          ????///??Contextual?information?about?the??being?used.?
          ????///?
          ????///?????The?result?of?the?call?to?.
          ????///?????This?value?is?typically?used?as?the?return?value?for?the?implementation?of?this?method.
          ????///?
          ????///??The?cancellation?token.?
          ????///?
          ????///?????The?result?that?EF?will?use.
          ????///?????A?normal?implementation?of?this?method?for?any?interceptor?that?is?not?attempting?to?change?the?result
          ????///?????is?to?return?the??value?passed?in.
          ????///?
          ????ValueTask<int>?SavedChangesAsync(
          ????????[NotNull]?SaveChangesCompletedEventData?eventData,
          ????????int?result,
          ????????CancellationToken?cancellationToken?=?default
          )
          ;

          ????///?
          ????///?????Called?when?an?exception?has?been?thrown?in?.
          ????///?

          ????///??Contextual?information?about?the?failure.?
          ????///??The?cancellation?token.?
          ????///??A??representing?the?asynchronous?operation.?
          ????Task?SaveChangesFailedAsync(
          ????????[NotNull]?DbContextErrorEventData?eventData,
          ????????CancellationToken?cancellationToken?=?default
          )
          ;
          }

          為了比較方便的實(shí)現(xiàn)自己需要的 Interceptor,微軟還提供了一個(gè) SaveChangesInterceptor 抽象類,這樣只需要繼承于這個(gè)類,重寫自己需要的方法即可,實(shí)現(xiàn)比較簡單,就是實(shí)現(xiàn)了 ISaveChangesInterceptor 接口,然后接口的實(shí)現(xiàn)基本都是空的虛方法,根據(jù)需要重寫即可

          源碼鏈接:https://github.com/dotnet/efcore/blob/v5.0.0/src/EFCore/Diagnostics/SaveChangesInterceptor.cs

          使用 SaveChangesInterceptor 實(shí)現(xiàn)自動(dòng)審計(jì)

          簡單寫了一個(gè)測試的審計(jì)攔截器

          public?class?AuditInterceptor?:?SaveChangesInterceptor
          {
          ????public?override?InterceptionResult<int>?SavingChanges(DbContextEventData?eventData,?InterceptionResult<int>?result)
          ????{
          ????????var?changesList?=?new?List();

          ????????foreach?(var?entry?in
          ?????????????????eventData.Context.ChangeTracker.Entries())
          ????????{
          ????????????if?(entry.State?==?EntityState.Added)
          ????????????{
          ????????????????changesList.Add(new?CompareModel()
          ????????????????????????????????{
          ????????????????????????????????????OriginalValue?=?null,
          ????????????????????????????????????NewValue?=?entry.CurrentValues.ToObject(),
          ????????????????????????????????});
          ????????????}
          ????????????else?if?(entry.State?==?EntityState.Deleted)
          ????????????{
          ????????????????changesList.Add(new?CompareModel()
          ????????????????????????????????{
          ????????????????????????????????????OriginalValue?=?entry.OriginalValues.ToObject(),
          ????????????????????????????????????NewValue?=?null,
          ????????????????????????????????});
          ????????????}
          ????????????else?if?(entry.State?==?EntityState.Modified)
          ????????????{
          ????????????????changesList.Add(new?CompareModel()
          ????????????????????????????????{
          ????????????????????????????????????OriginalValue?=?entry.OriginalValues.ToObject(),
          ????????????????????????????????????NewValue?=?entry.CurrentValues.ToObject(),
          ????????????????????????????????});
          ????????????}
          ????????????Console.WriteLine($"change?list:{changesList.ToJson()}");
          ????????}
          ????????return?base.SavingChanges(eventData,?result);
          ????}

          ????public?override?int?SavedChanges(SaveChangesCompletedEventData?eventData,?int?result)
          ????{
          ????????Console.WriteLine($"changes:{eventData.EntitiesSavedCount}");
          ????????return?base.SavedChanges(eventData,?result);
          ????}

          ????private?class?CompareModel
          ????{
          ????????public?object?OriginalValue?{?get;?set;?}

          ????????public?object?NewValue?{?get;?set;?}
          ????}
          }

          實(shí)際應(yīng)用的話還需要根據(jù)自己的場景做一些修改和測試

          測試 DbContext 示例,這里使用了一個(gè)簡單的 InMemory 做了一個(gè)測試:

          public?class?TestDbContext?:?DbContext
          {
          ????public?TestDbContext(DbContextOptions?dbContextOptions)?:?base(dbContextOptions)
          ????{
          ????}

          ????public?DbSet?Posts?{?get;?set;?}
          }

          public?class?Post
          {
          ????[Key]
          ????public?int?Id?{?get;?set;?}

          ????public?string?Author?{?get;?set;?}

          ????public?string?Title?{?get;?set;?}

          ????public?DateTime?PostedAt?{?get;?set;?}
          }

          測試代碼:

          var?services?=?new?ServiceCollection();
          services.AddDbContext(options?=>
          {
          ????options.UseInMemoryDatabase("Tests")
          ????????//.LogTo(Console.WriteLine)?//?EF?Core?5?中新的更簡潔的日志記錄方式
          ????????.AddInterceptors(new?AuditInterceptor())
          ????????;
          });
          using?var?provider?=?services.BuildServiceProvider();
          using?(var?scope?=?provider.CreateScope())
          {
          ????var?dbContext?=?scope.ServiceProvider.GetRequiredService();
          ????dbContext.Posts.Add(new?Post()?{?Id?=?1,?Author?=?"test",?Title?=?"test",?PostedAt?=?DateTime.UtcNow?});
          ????dbContext.SaveChanges();

          ????var?post?=?dbContext.Posts.Find(1);
          ????post.Author?=?"test2";
          ????dbContext.SaveChanges();

          ????dbContext.Posts.Remove(post);
          ????dbContext.SaveChanges();
          }

          輸出結(jié)果(輸出結(jié)果的如果數(shù)據(jù)為 null 就會(huì)被忽略掉,所以對(duì)于新增的數(shù)據(jù)實(shí)際是沒有原始值的,對(duì)于刪除的數(shù)據(jù)沒有新的值):

          More

          EF Core 5 還有很多新的特性,有需要的小伙伴可以看一下官方文檔的介紹~

          上述源碼可以在 Github 上獲取 https://github.com/WeihanLi/SamplesInPractice/blob/master/EF5Samples/SaveChangesInterceptorTest.cs

          Reference

          • https://github.com/dotnet/efcore
          • https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew#savechanges-interception-and-events
          • https://www.cnblogs.com/weihanli/p/13416219.html
          • https://github.com/WeihanLi/SamplesInPractice/blob/master/EF5Samples/SaveChangesInterceptorTest.cs


          瀏覽 104
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  超碰人妻中文字幕 | 日韩 人妻 精品 无码 欧美 | 一区二区三区四区在线 | 大黄片AAA | 日韩高清无码2023 |