ABP框架使用(版本3.3.1) - EntityFramework

复制代码
    public class Test : FullAuditedAggregateRootWithUser<Guid, AppUser>
    {
        public string TransactionNumber { get; set; }

        public virtual List<TestItem> TestItems { get; set; }
    }
    
    public class TestItem : AuditedEntity
    {
        public Guid TestId { get; set; }
        public Guid ItemId { get; set; }

        [ForeignKey("ItemId")]
        public Item Item { get; set; }
        public int Quantity { get; set; }

        public virtual List<TestSubItem> TestSubItems { get; set; }
        public override object[] GetKeys()
        {
            return new object[] { TestId,ItemId };
        }
    }
    
        public class TestSubItem : AuditedEntity
    {
        public Guid TestId { get; set; }
        public Guid ItemId { get; set; }

        [ForeignKey("TierId")]
        public Tier Tier { get; set; }
        public Guid TierId { get; set; }
        public int Quantity { get; set; }
        public override object[] GetKeys()
        {
            return new object[] { TestId, ItemId , TierId };
        }
    }
复制代码

1.当update TestItem的值 , 使用updateaync会报错

2021-03-27 18:51:43.422 +08:00 [ERR] The instance of entity type 'TestSubItem' cannot be tracked because another instance with the same key value for {'TestId', 'ItemId', 'SubItemId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

研究了好久,找不到方法override 这个类 StateManager.

用下面改变 EntityState 的方法是有时凑效有时不凑效

_context.Attach(modelPostedToController);
IEnumerable<EntityEntry> unchangedEntities = _context.ChangeTracker.Entries().Where(x => x.State == EntityState.Unchanged);
foreach(EntityEntry ee in unchangedEntities){
     ee.State = EntityState.Modified;
}
await _context.SaveChangesAsync();

最后的解决方案是先 remove 再 update, 而且一定不能用 DbContext.SaveChangesAsync()

要是用aynchu会报错

[ERR] A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.

复制代码
    public class TestItemRepository : EfCoreRepository<DRSDbContext, TestItem>, ITestItemRepository
    {

        public TestItemRepository(IDbContextProvider<DRSDbContext> dbContextProvider) : base(dbContextProvider)
        {

        }

        public Task Save(List<TestItem> entities)
        {
            DbContext.AttachRange(entities);
            DbContext.UpdateRange(entities);
            DbContext.SaveChanges();
            return Task.CompletedTask;
        }

        public Task Remove(List<TestItem> entities)
        {
            DbContext.AttachRange(entities);
            DbContext.RemoveRange(entities);
            DbContext.SaveChanges();
            return Task.CompletedTask;
        }

    }
复制代码

 

2.当update sub entity的时候,如果sub entity有soft delete的话就会报duplicate key错。

sub entity不可以有soft delete,才可以update成功。当做法总是会先删除旧的那条,再新增新的那条。所以从last modify time时间来看是不能正确反映真实情况的。

在abp没有找到DbContext如何触发到ChangeTracker的代码,翻看了下ef core的源代码,用的ReferenceEquals,无从改造大。

那么就还是在DbContext对ChangeTracker再做一层过滤,回归到Unchanged和Modified的State.

DbContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
protected override EntityChangeReport ApplyAbpConcepts()     {
          var changeReport = new EntityChangeReport();
          var addList = ChangeTracker.Entries().Where(o => o.State == EntityState.Added).ToList();
          var delList = ChangeTracker.Entries().Where(o => o.State == EntityState.Deleted).ToList();
          var addListKeys = addList.Select(o => GetEntityId(o.Entity)).ToList();
          var delListKeys = delList.Select(o => GetEntityId(o.Entity)).ToList();
          var addListHashCodes = addList.Select(o => o.Entity.GetHashCode()).ToList();
          var delListHashCodes = delList.Select(o => o.Entity.GetHashCode()).ToList();
          var modListKeys = addListKeys.Intersect(delListKeys);
          var modListHashCodes = addListHashCodes.Intersect(delListHashCodes);
 
       
          foreach (var entry in ChangeTracker.Entries().ToList())
          {
              var keys = GetEntityId(entry.Entity);
              var hashCodes = entry.Entity.GetHashCode();
              if (entry.State == EntityState.Added && modListHashCodes.Contains(hashCodes))
              {
                  entry.State = EntityState.Unchanged;
              }
              else if (entry.State == EntityState.Added && modListKeys.Contains(keys))
              {
                  entry.State = EntityState.Modified;
              }
              if (entry.State == EntityState.Deleted && modListKeys.Contains(keys))
                  continue;
              ApplyAbpConcepts(entry, changeReport);
          }
 
          return changeReport;
      }

 

这里因为Added entity 的LastModifyTime和Deleted entity 的LastModifyTime不一致,所以要改写sub entity的GetHashCode() 方法,才能判断出两者是UnChanged

复制代码
    public class ItemTier : AuditedEntity //FullAuditedEntity
    {
        public Guid ItemId { get; set; }
        
        public Guid TierId { get; set; }

        [ForeignKey("ItemId")]
        public virtual Item Item { get; set; }

        [ForeignKey("TierId")]
        public /*virtual*/  Tier Tier { get; set; }

        public bool Allow { get; set; }

        public override object[] GetKeys()
        {
            return new object[] { ItemId, TierId };
        }

        public override int GetHashCode()
        {
            unchecked // Overflow is fine, just wrap
            {
                int hash = 17;
                // Suitable nullity checks etc, of course :)
                hash = hash * 23 + ItemId.GetHashCode();
                hash = hash * 23 + TierId.GetHashCode();
                hash = hash * 23 + Allow.GetHashCode();
                return hash;
            }
        }

    }
复制代码

 

posted on   白马酒凉  阅读(272)  评论(0编辑  收藏  举报

编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示