2011-08-11 4 views
5

온라인으로 찾은 코드 스 니펫에서 감사 내역을 작성하고 있습니다. 내 SaveChanges 함수에 대한 호출에서 컨텍스트에 등록 된 모든 수정 된 엔티티를 반복하고 변경 사항에서 로그 항목을 작성합니다. (속성 수가 될 6 대신 8을 말할 것이다) DbEntityEntry.OriginalValues가 복잡한 속성을 채우지 않습니다.

foreach (DbEntityEntry modifiedEntity in this.ChangeTracker.Entries().Where(p => p.State == System.Data.EntityState.Added || p.State == System.Data.EntityState.Deleted || p.State == System.Data.EntityState.Modified)) 
     { 
      // For each changed record, get the audit record entries and add them 
      foreach(AuditLog x in GetAuditRecordsForChange(modifiedEntity, userId)) 
      { 
       this.AuditLog.Add(x); 
      } 
     } 

그때 수정 된 개체의 원래 값에 액세스하려고

모든 스칼라 속성이 채워집니다하지만 복잡한 것들은 존재하지 않습니다. 그런 다음 ToObject()을 호출하여 객체를 원래 상태로 만들지 만 복잡한 속성은 모두 null입니다.

modifiedEntity.OriginalValues.ToObject() 

에만 내 도메인 객체의 일부으로 발생하고, 그 객체는 항상 (내가 왜 확실하지 않다) 반면 ToObject() 호출 후 프록시로 표시하지만 것들에 대해 만든 프록시를하지 않아도 엔티티별로, 복잡한 속성이 잘 채워집니다. 애플리케이션 전반에 걸쳐 POCO 프록시를 정상적으로 사용할 때 게으른로드가 제대로 작동합니다.

OriginalValues ​​데이터의 일부로 채워지지 않은 이러한 복잡한 속성 중 하나를 변경하면 개체의 상태가 수정 됨으로 변경되지 않는다는 것을 알았습니다. 변경 내용 추적이 원래 값이 변경되었는지 확인하기 위해 현재 값을 반환합니다. 무슨 이해가 안되는 데이터가 여전히 SaveChanged에 유지됩니다 ??

편집 : 난 그냥 나타났습니다, 이 복잡한 특성을 채우는 않는 모델 객체가 문제의 복잡한 속성 (관례)이다는 즉 기본 키 엔티티에 의해 '복합 형'을 고려했다.

아이디어가 있으십니까?

답변

4

나는이 기사가 당신에게 약간의 통찰력을 줄 것이라고 믿는다. EF 4.1이 아니지만 많은 팁과 예제가 적용됩니다.

Complex Types and the New Change Tracking API

그 링크의 이름 인 섹션의 제목 튜토리얼을 통해 중간 전에 조금. 기본적으로 복잡한 유형의 원래 값에 액세스하려면 복합 속성을 지정하는 추가 함수를 추가하십시오.

var original = modifiedEntity.ComplexProperty(u => u.Address).OriginalValues 
+0

이 경우 유효한 감사 추적을 어떻게 구축 할 수 있는지 잘 모르겠습니다. 'myVehicle.Colour = myRepo.GetModelById (4)'와 같이 임의의 엔티티의 복합 속성을 수정하면 myVehicle 엔티티의 상태가 MODIFIED로 설정되지 않습니다. 이 변화를 발견 할 수있는 방법이 있어야합니다. 채워진 스칼라 값만 가진 객체를 만드는 경우 myEntity.OriginalValues.ToObject()를 사용하는 것에 질문합니다. 내가 뭘 놓치고 있니? – SeeNoWeevil

+0

조금 파고 들자 마자 관계 추적이라고 말하고있는 것 같습니다. 즉, 부모 개체의 속성에서 참조를 다른 자식 개체로 업데이트하면됩니다. 이 속성이 ID가없고 ID가없는 '복합 유형'이 아닌 실제 유형을 가리키고 있다고 가정하면이 속성은 _relationship_ 변경이며 일반 속성 변경이 아닙니다. 이러한 관계 변경에 대한 액세스 권한을 얻는 유일한 방법은 myObjectContext.ObjectStateManager.GetObjectStateEntries (..)를 통해 감사 로그로보고하는 방법을 잘 모르겠습니다. – SeeNoWeevil

1

더 파고, EF 변경 내용 추적이 내가 할 수있는 수정 된 개체에 대한 참조 또는 집합 형 속성에 대한 원래 값의 어떤 종류 (내가 틀렸다면 누군가가 나 수정하시기 바랍니다)

를 저장하지 않는 것 같다 예를 들어 내 Vehicle 엔티티가 VehicleColour 객체에 대한 참조를 제거한 다음 다시 추가하여 VehicleColour의 다른 인스턴스를 가리키는 지 확인합니다. 예를 들어 "Stardust Silver"라는 이름의 VehicleColour를 가리키고 "Azure Blue"가있는 점을 가리킨다는 것을 알 수 없습니다.

8

DbContext 대신 ObjectContext이 아닌 엔티티의 모든 구성원 이름을 가져 오려면 EntityType을 통해 구성원 목록에 액세스하십시오.

((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(dbEntry).EntitySet.ElementType.Members 

그런 다음 DbEntityEntry.Member(string propertyName) 메서드를 사용하여 DbMemberEntry를 가져올 수 있습니다.

엔티티의 구성원을 나타내는 개체를 가져옵니다. 반환 된 객체의 런타임 유형은 요청되는 멤버의 종류에 따라 다릅니다. 현재 지원되는 멤버 형식과 그 반환 형식은 참조 탐색 속성 (DbReferenceEntry), 컬렉션 탐색 속성 (DbCollectionEntry), 기본/스칼라 속성 (DbPropertyEntry) 및 복합 속성 (DbComplexPropertyEntry)입니다.

아래 코드 샘플은 복잡한 속성 수정을 기록하는 데 사용됩니다. 복잡한 속성 변경을 로깅 할 때 좀 더 섹시한 작업이있을 것입니다. 현재 변경된 내부 속성이 아닌 복잡한 속성 (JSON에 직렬화 됨) 전체를 로깅하고 있지만 작업이 완료됩니다.

private IEnumerable<AuditLogEntry> GetAuditLogEntries(DbEntityEntry dbEntry) 
{ 
    if (dbEntry.State == EntityState.Added) 
    { 
     return new AuditLogEntry { ... }; 
    } 

    if (dbEntry.State == EntityState.Deleted) 
    { 
     return new AuditLogEntry { ... }; 
    } 

    if (dbEntry.State == EntityState.Modified) 
    { 
     // Create one AuditLogEntry per updated field. 

     var list = new List<AuditLogEntry>(); 

     // We need to object state entry to do deeper things. 
     ObjectStateEntry objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(dbEntry); 

     // Iterate over the members (i.e. properties (including complex properties), references, collections) of the entity type 
     foreach (EdmMember member in ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(dbEntry).EntitySet.ElementType.Members) 
     { 
      var dbMemberEntry = dbEntry.Member(member.Name) as DbPropertyEntry; 
      if (dbMemberEntry == null || Equals(dbMemberEntry.OriginalValue, dbMemberEntry.CurrentValue)) 
      { 
       // Member entry isn't a property entry or it isn't modified. 
       continue; 
      } 

      string oldValue; 
      string newValue; 

      if (dbMemberEntry is DbComplexPropertyEntry) 
      { 
       // Bit a bit lazy here and just serialise the complex property to JSON rather than detect which inner properties have changed. 
       var complexProperty = (DbComplexPropertyEntry)dbMemberEntry; 
       oldValue = EntitySerialiser.Serialise(complexProperty.OriginalValue as IAuditableComplexType); 
       newValue = EntitySerialiser.Serialise(complexProperty.CurrentValue as IAuditableComplexType); 
      } 
      else 
      { 
       // It's just a plain property, get the old and new values. 
       var property = dbMemberEntry; 
       oldValue = property.OriginalValue.ToStringOrNull(); 
       newValue = property.CurrentValue.ToStringOrNull(); 
      } 

       list.Add(new AuditLogEntry 
         { 
          ..., 
          EventType = AuditEventType.Update, 
          ColumnName = member.Name, 
          OriginalValue = oldValue, 
          NewValue = newValue 
         }); 
     } 

     return list; 
    } 

    // Otherwise empty. 
    return Enumerable.Empty<AuditLogEntry>(); 
} 

나는 다른 해결책을보고 있습니다.

관련 문제