2011-08-16 30 views
9

Tôi có một lớp cơ sở mà tôi kế thừa từ đó có hai số không đến nhiều mối quan hệ với các đơn vị khác:ChangeTracker Entity Framework 4.1 - Giá trị gốc của Related Objects

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

Tôi gặp khó khăn khi các giá trị ban đầu cho các mối quan hệ (RelatedWebObjects & RelatedTags) khi xem xét các thực thể bằng cách sử dụng ChangeTracker trong SaveChanges. Tôi có thể thấy tất cả các giá trị vô hướng trước và sau, và tôi có thể thấy các mối quan hệ mới, nhưng tôi không thể nhìn thấy các giá trị cũ. Tôi đã thử sử dụng các phương pháp Thành viên và Bộ sưu tập, nhưng chúng chỉ hiển thị cho tôi các giá trị hiện tại; không phải là cũ. Ngoài ra tôi không thích sử dụng chúng bởi vì nó đòi hỏi tôi phải biết tên của thuộc tính điều hướng, không đủ chung chung.

Tôi có thể tìm thấy các đối tượng liên quan có mối quan hệ đang thay đổi, nhưng tất nhiên giá trị trong các đối tượng liên quan đó không thay đổi, do đó cũng không phải là bất kỳ trợ giúp nào.

Có cách nào sạch sẽ để tôi theo dõi các mối quan hệ trước đó của một thực thể trong SaveChanges bằng ChangeTracker không?

Dưới đây là phần mã mà tôi đang làm việc trên:

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

Các đối tượng liên quan cũng đang được thay đổi do ObjectStateManager theo dõi và bạn sẽ có thể nhận được các mục nhập trạng thái đối tượng giống như cách bạn nhận được đối tượng chính của mình. Nếu bạn đăng mã mà bạn đang gặp khó khăn, tôi có thể giúp bạn. –

+0

Cảm ơn một lần nữa Morteza ... Tôi đã đăng phần mã mà tôi đang làm việc với ngay bây giờ - hãy tha thứ cho sự cẩu thả của nó; nó chưa được tái cấu trúc - chỉ cố gắng làm cho nó hoạt động. Tôi có thể nhận được các thuộc tính vô hướng của các đối tượng mà không gặp bất kỳ vấn đề nào bằng cách sử dụng các chuỗi IEnumerable: entity.OriginalValues.PropertyNames & entity.CurrentValues.PropertyNames. Tôi gặp sự cố với entity.Collection() và entity.Member(). Nên sử dụng cái nào cho những gì tôi đang cố gắng hoàn thành? Có cách nào để làm cho điều này chung chung để tôi không phải mã hóa cứng tên bộ sưu tập? Phản ánh có thể? – DMC

+0

Tôi không ở vị trí nào để thử điều này cho đến ngày mai ... một trong hai bạn có biết cách tôi có thể chia điểm giữa hai câu trả lời không? bởi vì tôi nghĩ rằng cả hai đều cung cấp thông tin rất có giá trị. – DMC

Trả lời

4

Vì EF thay đổi theo dõi tất cả các đối tượng trong đồ thị của bạn, bạn luôn có thể vượt qua bất kỳ trường hợp trong đồ thị dưới để theo dõi sự thay đổi và nó sẽ cung cấp cho bạn các giá trị theo dõi thay đổi. Ví dụ, đoạn mã sau sẽ nhận được các giá trị hiện tại/gốc của tài sản chuyển hướng của 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; 

Hoặc bạn có thể áp dụng các thủ thuật tương tự khi bạn đang đối phó với một tài sản sưu tập kiểu navigation:

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

foreach (WebObject item in currentAuditObject.RelatedWebObjects) 
{ 
    var currValues = this.Entry(item).CurrentValues; 
} 
+0

cảm ơn một lần nữa Morteza! tôi đánh giá cao việc bạn quay lại với tôi một cách nhanh chóng. cũng cảm ơn một lần nữa cho blog! – DMC

12

Điều này hơi khó. Trước hết bạn phải khác nhau two types of relationships được cung cấp bởi EF:

  • hiệp hội độc lập (tất cả các mối quan hệ nhiều-nhiều và một số one-to-many) (tất cả các mối quan hệ one-to-one
  • gắn chìa khóa ngoại và một số một-nhiều)

Bây giờ nếu bạn muốn biết giá trị trước của hiệp hội khóa nước ngoài, bạn chỉ cần theo dõi những thay đổi trong thực thể phụ thuộc nơi bạn đã tiếp xúc với khóa ngoại - điều này hoàn toàn giống như theo dõi bất kỳ thay đổi bất động sản nào khác.

Nếu bạn muốn theo dõi các thay đổi trong liên kết độc lập, tình huống sẽ trở nên khó khăn hơn vì DbContext API doesn't provide operations to track them. Bạn phải hoàn nguyên về số ObjectContext API và ObjectStateManager.

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

Bây giờ bạn có quyền truy cập vào ObjectStateEntry trường hợp cho mối quan hệ. Những trường hợp này sẽ không bao giờ có trạng thái Modified. Chúng sẽ là Added, Deleted hoặc Unchanged vì "sửa đổi" được xử lý khi xóa mối quan hệ cũ và thêm quan hệ mới. ObjectStateEntry cũng chứa các bộ sưu tập CurrentValuesOriginalValues. Những bộ sưu tập này cũng nên chứa hai mục đại diện cho mỗi đối tượng EntityKey ở một bên của mối quan hệ.

+0

cảm ơn bạn rất nhiều vì sự giúp đỡ của bạn! tôi có thể thấy rằng nó hoạt động. tôi cũng đã học về toán tử bitwise ~ - không bao giờ được sử dụng trước đó. bạn có biết nếu có thể chỉ định điểm cho cả bạn và Morteza? câu trả lời của anh ấy xuất hiện đầu tiên và hoạt động cũng như tôi đã liên hệ với anh ấy thông qua blog của anh ấy, nhưng tôi muốn cả hai bạn nhận được tín dụng ... – DMC

+0

@DMC: Bạn chỉ có thể đánh dấu một câu trả lời là được chấp nhận nhưng tùy thuộc vào danh tiếng của bạn cast upvotes đến nhiều câu trả lời như bạn muốn. –

+0

xin lỗi - sẽ đưa nó cho Morteza bởi vì anh ấy đã nhận nó trong đầu tiên và tôi đã đi ra khỏi con đường của tôi để liên lạc trực tiếp với anh về điều này. nhưng tôi đã cung cấp cho bạn một upvote cho điều này. – DMC

Các vấn đề liên quan