9

我有一个我继承的基类,它与其他实体有两个零到多关系:

public abstract class WebObject
{
    public WebObject()
    {
        RelatedTags = new List<Tag>();
        RelatedWebObjects = new List<WebObject>();
    }

    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    public string MetaKeywords { get; set; }
    public string MetaDescription { get; set; }

    [InverseProperty("WebObjects")]
    public virtual WebSite WebSite { get; set; }

    [Required(ErrorMessage = "Every WebObject must be associated with a WebSite.")]
    public Guid WebSiteId { get; set; }

    public virtual ICollection<Tag> RelatedTags { get; set; }
    public IList<Guid> RelatedTagIds { get; set; }
    public virtual ICollection<WebObject> RelatedWebObjects { get; set; }
    public IList<Guid> RelatedWebObjectIds { get; set; }
}

在 SaveChanges 期间查看使用 ChangeTracker 的实体时,我很难获得这些关系(RelatedWebObjects 和 RelatedTags)的原始值。我可以看到之前和之后的所有标量值,我可以看到新的关系,但我看不到旧的。我试过使用 Member 和 Collection 方法,但那些只显示当前值;不是旧的。我也不喜欢使用这些,因为它需要我知道导航属性的名称,这不够通用。

我可以找到关系正在改变的相关对象,但是这些相关对象中的值当然没有改变,所以这也没有任何帮助。

在使用 ChangeTracker 进行 SaveChanges 期间,我是否有一些干净的方法可以跟踪实体的先前关系?

以下是我正在处理的代码部分:

    public override int SaveChanges()
    {
        List<AuditObject> auditTrailList = new List<AuditObject>();

        foreach (DbEntityEntry entity in ChangeTracker.Entries().Where(obj => { return obj.State == EntityState.Added || obj.State == EntityState.Modified || obj.State == EntityState.Deleted; }))
        {
            if (!(entity.Entity is AuditObject))
            {
                AuditObject auditObject = new AuditObject();

                auditObject.Id = Guid.NewGuid();

                auditObject.RevisionStamp = DateTime.Now;

                auditObject.UserName = HttpContext.Current.User.Identity.Name;

                auditObject.EntityType = Utilities.GetCleanClassNameIfProxyClass(entity.Entity.GetType().Name);

                if (entity.State == EntityState.Added)
                    auditObject.Action = EntityState.Added.ToString();
                else if (entity.State == EntityState.Modified)
                    auditObject.Action = EntityState.Modified.ToString();
                else if (entity.State == EntityState.Deleted)
                    auditObject.Action = EntityState.Deleted.ToString();

                DbMemberEntry t1 = entity.Member("RelatedWebObjects");
                // cannot find original relationship collection...

                DbCollectionEntry t2 = entity.Collection("RelatedWebObjects");
                // cannot find original relationship collection...

                if (entity.State == EntityState.Added || entity.State == EntityState.Modified)
                {
                    XDocument currentValues = new XDocument(new XElement(auditObject.EntityType));

                    foreach (string propertyName in entity.CurrentValues.PropertyNames)
                    {
                        currentValues.Root.Add(new XElement(propertyName, entity.CurrentValues[propertyName]));
                    }

                    auditObject.NewData = Regex.Replace(currentValues.ToString(), @"\r\n+", " ");
                }

                if (entity.State == EntityState.Modified || entity.State == EntityState.Deleted)
                {
                    XDocument originalValues = new XDocument(new XElement(auditObject.EntityType));

                    foreach (string propertyName in entity.OriginalValues.PropertyNames)
                    {
                        originalValues.Root.Add(new XElement(propertyName, entity.OriginalValues[propertyName]));
                    }

                    auditObject.OldData = Regex.Replace(originalValues.ToString(), @"\r\n+", " ");
                }

                auditTrailList.Add(auditObject);
            }
        }

        foreach (var audit in auditTrailList)
            this.AuditObjects.Add(audit);

        return base.SaveChanges();
    }
4

2 回答 2

13

嗯,这有点困难。首先,您必须区分 EF 提供的两种类型的关系:

  • 独立关联(所有多对多关系和一些一对多)
  • 外键关联(所有一对一关系和一些一对多)

现在,如果您想知道外键关联的先前值,您只需要跟踪已暴露外键属性的依赖实体中的更改 - 这与跟踪任何其他属性更改完全相同。

如果您想跟踪独立关联中的更改,情况会变得更加困难,因为 DbContext API不提供跟踪它们的操作。您必须恢复到ObjectContextAPI 及其ObjectStateManager.

ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
foreach (ObjectStateEntry entry = objectContext.ObjectStateManager
                                               .GetObjectStateEntries(~EntityState.Detached)
                                               .Where(e => e.IsRelationship))
{
    // Track changes here
}

现在您可以访问ObjectStateEntry该关系的实例。这些实例不应该有 state Modified。它们将是AddedDeletedUnchanged因为“修改”被处理为删除旧关系并添加新关系。ObjectStateEntry还包含CurrentValuesOriginalValues集合。这些集合还应该包含两个项目,每个项目代表EntityKey关系一侧的实体。

于 2011-08-20T17:57:13.653 回答
4

因为 EF 更改跟踪图表中的每个对象,所以您始终可以将图表中的任何实例传递给更改跟踪器,它将为您提供更改跟踪值。例如,以下代码将获取 AuditObject 导航属性的原始/当前值:

DbMemberEntry t1 = entity.Member("RelatedWebObjects");
// cannot find original relationship collection....

AuditObject currentAuditObject = (AuditObject) entity;
var currValues = this.Entry(currentAuditObject.RelatedWebObjects).CurrentValues;
var orgValues = this.Entry(currentAuditObject.RelatedWebObjects).OriginalValues;

或者,您可以在处理集合类型导航属性时应用相同的技巧:

DbCollectionEntry t2 = entity.Collection("RelatedWebObjects");
// cannot find original relationship collection....

foreach (WebObject item in currentAuditObject.RelatedWebObjects)
{
    var currValues = this.Entry(item).CurrentValues;
}
于 2011-08-20T16:47:21.490 回答