如何在DbContext中的SaveChanges中为关系(连接表)添加自定义处理

本文关键字:连接 添加 处理 自定义 关系 DbContext 中的 SaveChanges | 更新日期: 2023-09-27 18:04:53

我有一个自定义DbContext,我用它来记录通过EF对数据库进行的任何更改(插入、更新、删除)。它通过在DbContext上重写SaveChanges方法来工作。它的工作原理大致如下:

public override int SaveChanges()
{
    foreach (DbEntityEntry entry in this.ChangeTracker.Entries())
    {
       Log(entry);
    }
    return base.SaveChanges();
}

对于实体(例如,Client或Employee)的更改,这工作得相当好。但是,我很难弄清楚如何记录实体之间关系的更改。例如,有一个ClientEmployee连接表,它有一个ClientId和一个EmployeeId。EF正确地解释了这种关系,并在每个表上放置了另一个表的virtual ICollection。我怎样才能找到在SaveChanges()期间添加和删除了哪些关系?我需要能够记录它们(无论是添加还是删除),即使唯一的区别是在连接表中创建或删除了一行。

我一直使用的临时解决方案是通过添加主键并在代码中手动创建行来使我的连接表成为实体,但这很麻烦,而且感觉不对。在上面的例子中,Employees将拥有一个virtual ICollection<ClientEmployee>,而每个ClientEmployee实体将拥有一个Client和一个Employee。这样可以完成工作,但是它增加了开发人员的开销和所有必须学习和遵循这种非正统模式的开发人员出错的可能性。

是否有任何方法可以预览关系的更改(连接表中插入或删除的每个单独行,如果可能的话),即使更改不是严格意义上的实体?

如何在DbContext中的SaveChanges中为关系(连接表)添加自定义处理

是的,可以做到。关键是将DbContext转换为其底层的ObjectContext。对于每个Added实体,您将只记录添加的相关实体。对于每个Modified实体,您将需要记录删除和添加的相关实体。

public override int SaveChanges()
{
    foreach (DbEntityEntry entry in this.ChangeTracker.Entries())
    {
       Log(entry);
       switch(entry.State)
       {
          case EntityState.Added:
             Log(GetRelatedEntityKeys(this, entry, EntityState.Added));
             break;
          case EntityState.Modified:
             Log(GetRelatedEntityKeys(this, entry, EntityState.Added));
             Log(GetRelatedEntityKeys(this, entry, EntityState.Deleted));
             break;
       }
    }
    return base.SaveChanges();
}

检查关系的代码为:

private static IList<EntityKey> GetRelatedEntityKeys(DbContext context, DbEntityEntry entry, EntityState entityState)
{
    ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
    ObjectStateManager objectStateManager = objectContext.ObjectStateManager;
    ObjectStateEntry pivotEntityStateEntry;
    if (!objectStateManager.TryGetObjectStateEntry(entry.Entity, out pivotEntityStateEntry))
    {
        return null;
    }
    EntityKey pivotEntityKey = pivotEntityStateEntry.EntityKey;
    if (entityState == EntityState.Deleted)
    {
        return objectStateManager.GetObjectStateEntries(EntityState.Deleted)
            .Where(e => e.IsRelationship && ((EntityKey)e.OriginalValues[0] == pivotEntityKey || (EntityKey)e.OriginalValues[1] == pivotEntityKey))
            .Select(e => (EntityKey)e.OriginalValues[0] == pivotEntityKey ? (EntityKey)e.OriginalValues[1] : (EntityKey)e.OriginalValues[0])
            .ToList();
    }
    else
    {
        return objectStateManager.GetObjectStateEntries(EntityState.Added)
            .Where(e => e.IsRelationship && ((EntityKey)e.CurrentValues[0] == pivotEntityKey || (EntityKey)e.CurrentValues[1] == pivotEntityKey))
            .Select(e => (EntityKey)e.CurrentValues[0] == pivotEntityKey ? (EntityKey)e.CurrentValues[1] : (EntityKey)e.CurrentValues[0])
            .ToList();
    }
}

如果你需要DbEntityEntry而不是EntityKey,你可以这样查找实体:

private static DbEntityEntry GetRelatedEntityByKey(DbContext context, EntityKey relatedEntityKey)
{
    ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
    object relatedEntity;
    if (objectContext.TryGetObjectByKey(relatedEntityKey, out relatedEntity))
    {
        return context.Entry(relatedEntity);
    }
    return null;
}

您将需要为相关实体列表添加日志记录功能,但是看起来您已经掌握了代码的日志记录部分。