2011-08-16 16 views
9

Ho una classe base che ho ereditato da quella ha due zero a molti rapporti con altre entità:ChangeTracker Entity Framework 4.1 - I valori originali di oggetti correlati

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

Sto avendo difficoltà a ottenere i valori originali per questi relazioni (RelatedWebObjects & RelatedTags) quando si guardano le entità che utilizzano ChangeTracker durante SaveChanges. Posso vedere tutti i valori scalari prima e dopo, e posso vedere le nuove relazioni, ma non riesco a vedere quelle vecchie. Ho provato a utilizzare i metodi Member e Collection, ma solo quelli mostrano i valori correnti; non il vecchio. Inoltre non mi piace usare quelli perché mi richiede di conoscere il nome della proprietà di navigazione, che non è abbastanza generico.

Sono in grado di trovare gli oggetti correlati di cui si sta modificando la relazione, ma ovviamente i valori all'interno di tali oggetti correlati non cambiano, quindi neanche questo è di aiuto.

C'è qualche modo pulito per me per tenere traccia delle relazioni precedenti di un'entità durante SaveChanges con ChangeTracker?

Di seguito è la sezione di codice che sto lavorando su:

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

Gli oggetti correlati vengono modificati anche dall'ObjectStateManager e si dovrebbe essere in grado di ottenere le voci dello stato dell'oggetto proprio come si ottiene per l'oggetto principale. Se pubblichi il codice con cui stai lottando, potrei essere in grado di aiutarti. –

+0

Grazie ancora Morteza ... Ho postato la sezione di codice con cui sto lavorando in questo momento - per favore scusate la sciatteria di esso; non è stato affatto rifatto - solo cercando di farlo funzionare. È possibile ottenere le proprietà scalari degli oggetti senza problemi utilizzando le stringhe IEnumerable: entity.OriginalValues.PropertyNames e entity.CurrentValues.PropertyNames. Ho problemi con entity.Collection() e entity.Member(). Quale dovrebbe essere usato per quello che sto cercando di realizzare? C'è un modo per rendere questo generico in modo da non dover codificare i nomi delle collezioni? Riflessione forse? – DMC

+0

non sono in grado di provare questo fino a domani ... qualcuno di voi sa come posso dividere i punti tra due risposte? perché penso che entrambi abbiate dato informazioni preziose. – DMC

risposta

4

Perché il cambiamento EF tiene traccia di ogni oggetto nel grafico, si può sempre passare qualsiasi istanza nel grafico al cambiamento tracker e sarà darti i valori di tracciamento delle modifiche. Ad esempio, il seguente codice otterrà i valori originali/corrente di proprietà di navigazione del 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; 

Oppure si può applicare lo stesso trucco quando si tratta di una proprietà della raccolta tipo di navigazione:

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

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

grazie ancora Morteza! apprezzo che tu mi abbia risposto così velocemente. anche grazie ancora per il blog! – DMC

12

Beh, questo è un po 'difficile. Prima di tutto si deve differire two types of relationships offerto da EF:

  • associazione indipendente (tutti i molti-a-molti rapporti e alcuni uno-a-molti)
  • Esteri associazione chiave (tutte le relazioni one-to-one

Ora, se si desidera conoscere il valore precedente dell'associazione di chiavi esterne, è sufficiente tenere traccia delle modifiche nell'entità dipendente in cui è stata visualizzata la proprietà di chiave esterna - questo è esattamente lo stesso di tracciamento di qualsiasi altra modifica di proprietà.

Se si desidera tenere traccia delle modifiche in un'associazione indipendente, la situazione diventa più difficile perché l'API DbContext doesn't provide operations to track them. È necessario ripristinare l'API ObjectContext e il suo ObjectStateManager.

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

Ora è possibile accedere alle istanze ObjectStateEntry per la relazione. Queste istanze non dovrebbero mai avere lo stato Modified. Saranno o Added, Deleted o Unchanged perché "modifica" viene elaborata come eliminazione della vecchia relazione e aggiunta di una nuova. ObjectStateEntry contiene anche collezioni CurrentValues e OriginalValues. Queste raccolte dovrebbero contenere anche due voci ciascuna rappresentante EntityKey di entità su un lato della relazione.

+0

grazie mille per il tuo aiuto! posso vedere che questo funziona. ho anche imparato a conoscere l'operatore bitwise ~, mai usato prima. sai se è possibile assegnare punti sia a te che a Morteza? la sua risposta è arrivata per prima e funziona anche io e ho contattato specificamente tramite il suo blog, ma voglio che entrambi otteniate credito ... – DMC

+0

@DMC: è possibile contrassegnare solo una risposta come accettata ma a seconda della vostra reputazione è anche possibile invoca le risposte per quante più risposte vuoi. –

+0

scusa, lo darò a Morteza perché l'ha ottenuto per primo e ho cercato di contattarlo direttamente a riguardo. ma ti ho dato un upvote per questo. – DMC

Problemi correlati