2011-08-16 120 views
9

我有一个基类,我从继承有两个零到与其他实体一对多的关系:ChangeTracker实体框架4.1 - 相关的原始值的对象

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)。我可以在前后看到所有的标量值,我可以看到新的关系,但我看不到旧的关系。我试过使用成员和集合方法,但这些方法只显示当前值;不是旧的。另外我不喜欢使用这些,因为它要求我知道导航属性的名称,这是不够通用的。

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

有没有一些干净的方式让我跟踪SaveChanges与ChangeTracker之间的实体之前的关系?

下面是部分代码是我的工作:

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(); 
    } 
+0

相关的对象也被ObjectStateManager跟踪到,你应该能够获得它们的对象状态条目,就像你如何获得它的主要对象一样。如果您发布了您正在努力的代码,我可能会提供帮助。 –

+0

再次感谢Morteza ...我发布了我现在正在使用的代码段 - 请原谅它的不合理性;它一直没有被重构 - 只是试图让它起作用。 我可以使用IEnumerable字符串没有任何问题的对象的标量属性:entity.OriginalValues.PropertyNames&entity.CurrentValues.PropertyNames。我遇到了entity.Collection()和entity.Member()的问题。 哪些应该用于我想要完成的工作?有没有办法使这个通用的,所以我不必硬编码的集合名称?反思也许? – DMC

+0

我不能在明天之前尝试这个......你们中的任何一个都知道我可以如何在两个答案之间分开点?因为我认为你们都提供了非常有价值的信息。 – DMC

回答

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; 
} 
+0

再次感谢Morteza!我很感激你很快回到我身边。还要再次感谢博客! – DMC

12

好吧,这有点困难。首先,你有什么不同的EF提供two types of relationships

  • 独立协会(一个都不少一对多的关系,有一种一对多)
  • 外键关联(全一到一个关系和一些一对多)

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

如果您想跟踪独立关联中的更改,情况将变得更加困难,因为DbContext API doesn't provide operations to track them。您必须恢复到ObjectContext API及其ObjectStateManager

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

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

+0

非常感谢您的帮助!我可以看到这是有效的。我还了解了按位运算符 - 以前从未使用过。你知道是否有可能为你和Morteza分配点数?他的回答是第一个,也有效,我特意通过他的博客与他联系,但我希望你们都得到信用... – DMC

+0

@DMC:你可以只标记一个答案,但取决于你的声望,你也可以根据您的需要投出尽可能多的答案。 –

+0

抱歉 - 要把它交给Morteza,因为他先得到它,我竭尽全力直接与他联系。但是我确实给了你一个赞成。 – DMC