none
ObjectStateEntry Original Values Have Current Updated Values RRS feed

  • Question

  • Guys, Hi

    Many thanks for viewing this question. 

    I have the following issue and wonder if someone can help me. I am using EF4.1 and RIA services with repository pattern. I am trying to implement auditing in the repository base. When an object is modified in the application (Silverlight) and when attached to the datacontext, the original values have the updated values. Please see below the output and code. 

    Many thanks again.

    public virtual IOperationStatus Update<T>(T entity, params string[] propsToUpdate) where T : class, IObjectWithChangeTracker
    		{
    			IOperationStatus opStatus = new OperationStatus { Status = true };
    
    			using (ILogger logger = new EventLogger())
    			{
    				try
    				{
    					entity.ChangeTracker.State = ObjectState.Modified;
    					ObjectSet<T> objectSet = DataContext.CreateObjectSet<T>();
    					objectSet.AttachAsModified(entity);
    
    					ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
    
    					ObjectStateEntry entry = DataContext.ObjectStateManager.GetObjectStateEntry(entity);
    
    					logger.Log("********** Original Values (IN UPDATE) ***********");
    
    					if (entry.OriginalValues != null)
    					{
    
    						for (int i = 0; i < entry.OriginalValues.FieldCount; i++)
    						{
    							logger.Log(string.Format("Original Property Value : {0}", entry.OriginalValues[i]));
    							logger.Log(string.Format("Current Property Value : {0}", entry.CurrentValues[i]));
    						}
    
    						logger.Log("Change Tracker Original");
    						
    					}
    
    					logger.Log("********** End Original Values (IN UPDATE) ***********");
    
    					if (propsToUpdate != null && propsToUpdate.Length > 0)
    					{
    						//Control updating specific properties
    						foreach (var propName in propsToUpdate)
    						{
    							entry.SetModifiedProperty(propName);
    						}
    					}
    					else
    					{
    						entry.SetModified();
    						this.SetEntryModified(entry);
    
    					}
    					opStatus.Status = true; // DataContext.SaveChanges() > 0;
    				}
    				catch (Exception exp)
    				{
    					logger.LogError(exp);
    					opStatus = OperationStatus.CreateFromException("Error updating " + typeof(T) + ".", exp);
    				}
    			}
    
    			return opStatus;
    		}
    


    Output:

    2011-12-03 18:34:28.566:myApp:34:********** Original Values (IN UPDATE) ***********

    2011-12-03 18:34:28.568:myApp:34:Original Property ID Value : 3000

    2011-12-03 18:34:28.569:myApp:34:Current Property ID Value : 3000

    2011-12-03 18:34:28.570:myApp:34:Original Property FirstName Value : Steve

    2011-12-03 18:34:28.570:myApp:34:Current Property FirstName Value : Steve

    2011-12-03 18:34:28.571:myApp:34:Original Property LastName Value : Philips

    2011-12-03 18:34:28.574:myApp:34:Current Property LastName Value : Philips

    2011-12-03 18:34:28.580:myApp:34:Original Property CreateDate Value : 02/12/2011 11:57:01

    2011-12-03 18:34:28.581:myApp:34:Current Property CreateDate Value : 02/12/2011 11:57:01

    2011-12-03 18:34:28.583:myApp:34:Original Property ModifiedDate Value : 03/12/2011 18:34:28

    2011-12-03 18:34:28.584:myApp:34:Current Property ModifiedDate Value : 03/12/2011 18:34:28

    2011-12-03 18:34:28.585:myApp:34:********** End Original Values (IN UPDATE) ***********

     

    Saturday, December 3, 2011 6:39 PM

Answers

  • Alan, Hi

    Thanks for your input. It is very interesting that Original values are not available even when we have the "RoundTripOriginal" attribute set for the entity. What I ended up doing is having a dictionary collection in the repository base and populate the value from database. Something like below and then in the save, retrieve the original from collection based on the EntityKey. Though I am not sure if this is elegant solution, given the limitations I have, this does solve the problem. If anyone have better solution, please let me know.

     

    private Dictionary<EntityKey, object> OriginalValues { get; set; }

     

    public virtual IOperationStatus Update<T>(T entity, params string[] propsToUpdate) where T : class , IObjectWithChangeTracker
    {
    			IOperationStatus opStatus = new OperationStatus { Status = true };
     
    			using (ILogger logger = new EventLogger())
    			{
    				try
    				{
    					entity.ChangeTracker.State = ObjectState.Modified;
     
    					if (OriginalValues == null) OriginalValues = new Dictionary<EntityKeyobject>();
     
    					ObjectSet<T> objectSet = DataContext.CreateObjectSet<T>();
    					objectSet.AttachAsModified(entity);
     
    					ObjectStateEntry entry = DataContext.ObjectStateManager.GetObjectStateEntry(entity);
     
    					if (typeof(T).GetCustomAttributes(typeof(AuditRequiredAttribute), true).Length > 0)
    					{
    						if (!OriginalValues.ContainsKey(entry.EntityKey))
    						{
    							OriginalValues.Add(entry.EntityKey, GetOriginalEntity(entry, entity));
    						}
    						else
    						{
    							OriginalValues[entry.EntityKey] = GetOriginalEntity(entry, entity);
    						}
    					}
     
    					if (propsToUpdate != null && propsToUpdate.Length > 0)
    					{
    						//Control updating specific properties
    						foreach (var propName in propsToUpdate)
    						{
    							entry.SetModifiedProperty(propName);
    						}
    					}
    					else
    					{
    						entry.SetModified();
    						this.SetEntryModified(entry);
     
    					}
    					opStatus.Status = true// DataContext.SaveChanges() > 0;
    				}
    				catch (Exception exp)
    				{
    					logger.LogError(exp);
    					opStatus = OperationStatus.CreateFromException("Error updating " + typeof(T) + ".", exp);
    				}
    			}
     
    			return opStatus;
    		}
    public virtual IOperationStatus SaveChanges()
    		{
    			using (ILogger logger = new EventLogger())
    			{
    				//UserId = userId;
    				IOperationStatus opStatus = new OperationStatus();
    				opStatus.Status = true;
    				try
    				{
    					bool saveAudit = false;
    					using (var tran = new TransactionScope())
    					{
    						IEnumerable<ObjectStateEntry> changedEntries = DataContext.ObjectStateManager.GetObjectStateEntries(
    							EntityState.Added |
    							EntityState.Modified | EntityState.Deleted)
    							.Where(c => c.Entity.GetType().GetCustomAttributes(typeof(AuditRequiredAttribute), true).Length > 0);
     
    						Dictionary<ObjectStateEntryEntityState> auditableEntries = new Dictionary<ObjectStateEntryEntityState>();
    						foreach (var objectStateEntry in changedEntries)
    						{
    							auditableEntries.Add(objectStateEntry, objectStateEntry.State);
    						}
     
    						//grab deletes
    						foreach (var objectStateEntry in auditableEntries.Where(e => e.Value == EntityState.Deleted))
    						{
    							Audit audit = GetAuditObject(objectStateEntry.Key, objectStateEntry.Value);
    							if (audit != null) Insert(audit);
     
    						}
     
    						//grab Updates
    						foreach (var objectStateEntry in auditableEntries.Where(e => e.Value == EntityState.Modified))
    						{
    							Audit audit = GetAuditObject(objectStateEntry.Key, objectStateEntry.Value);
    							if (audit != null) Insert(audit);
    						}
     
    						this.DataContext.SaveChanges();
     
    						//grab Inserts
    						foreach (var objectStateEntry in auditableEntries.Where(e => e.Value == EntityState.Added))
    						{
    							Audit audit = GetAuditObject(objectStateEntry.Key, objectStateEntry.Value);
    							if (audit != null) Insert(audit);
    							saveAudit = true;
    						}
     
    						if (saveAudit) this.DataContext.SaveChanges(); //to update the Audit entries
     
    						tran.Complete();
    					}
    				}
    				catch (Exception ex)
    				{
    					logger.LogError(ex);
    					return OperationStatus.CreateFromException("SaveChanges Failed", ex);
    				}
    				return opStatus;
    			}
    		}
    private T GetOriginalEntity<T>(ObjectStateEntry entry, T entity) where T : classIObjectWithChangeTracker
    		{
    			using (ObjectContext context = new ObjectContext(DataContext.Connection.ConnectionString))
    			{
    				ObjectSet<T> objectSet = context.CreateObjectSet<T>();
    				return context.GetObjectByKey(entry.EntityKey) as T;
    			}
    		}

     

    Tuesday, December 6, 2011 6:04 PM

All replies

  • Hi MSethuraman,

    Welcome!

    The Attach(AttachAsModified) won't retrieve the database values. If you test Select method to retrieve from database, you will find the original values.

    Have a nice day.


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, December 5, 2011 8:01 AM
    Moderator
  • Alan, Hi

    Thanks for your input. It is very interesting that Original values are not available even when we have the "RoundTripOriginal" attribute set for the entity. What I ended up doing is having a dictionary collection in the repository base and populate the value from database. Something like below and then in the save, retrieve the original from collection based on the EntityKey. Though I am not sure if this is elegant solution, given the limitations I have, this does solve the problem. If anyone have better solution, please let me know.

     

    private Dictionary<EntityKey, object> OriginalValues { get; set; }

     

    public virtual IOperationStatus Update<T>(T entity, params string[] propsToUpdate) where T : class , IObjectWithChangeTracker
    {
    			IOperationStatus opStatus = new OperationStatus { Status = true };
     
    			using (ILogger logger = new EventLogger())
    			{
    				try
    				{
    					entity.ChangeTracker.State = ObjectState.Modified;
     
    					if (OriginalValues == null) OriginalValues = new Dictionary<EntityKeyobject>();
     
    					ObjectSet<T> objectSet = DataContext.CreateObjectSet<T>();
    					objectSet.AttachAsModified(entity);
     
    					ObjectStateEntry entry = DataContext.ObjectStateManager.GetObjectStateEntry(entity);
     
    					if (typeof(T).GetCustomAttributes(typeof(AuditRequiredAttribute), true).Length > 0)
    					{
    						if (!OriginalValues.ContainsKey(entry.EntityKey))
    						{
    							OriginalValues.Add(entry.EntityKey, GetOriginalEntity(entry, entity));
    						}
    						else
    						{
    							OriginalValues[entry.EntityKey] = GetOriginalEntity(entry, entity);
    						}
    					}
     
    					if (propsToUpdate != null && propsToUpdate.Length > 0)
    					{
    						//Control updating specific properties
    						foreach (var propName in propsToUpdate)
    						{
    							entry.SetModifiedProperty(propName);
    						}
    					}
    					else
    					{
    						entry.SetModified();
    						this.SetEntryModified(entry);
     
    					}
    					opStatus.Status = true// DataContext.SaveChanges() > 0;
    				}
    				catch (Exception exp)
    				{
    					logger.LogError(exp);
    					opStatus = OperationStatus.CreateFromException("Error updating " + typeof(T) + ".", exp);
    				}
    			}
     
    			return opStatus;
    		}
    public virtual IOperationStatus SaveChanges()
    		{
    			using (ILogger logger = new EventLogger())
    			{
    				//UserId = userId;
    				IOperationStatus opStatus = new OperationStatus();
    				opStatus.Status = true;
    				try
    				{
    					bool saveAudit = false;
    					using (var tran = new TransactionScope())
    					{
    						IEnumerable<ObjectStateEntry> changedEntries = DataContext.ObjectStateManager.GetObjectStateEntries(
    							EntityState.Added |
    							EntityState.Modified | EntityState.Deleted)
    							.Where(c => c.Entity.GetType().GetCustomAttributes(typeof(AuditRequiredAttribute), true).Length > 0);
     
    						Dictionary<ObjectStateEntryEntityState> auditableEntries = new Dictionary<ObjectStateEntryEntityState>();
    						foreach (var objectStateEntry in changedEntries)
    						{
    							auditableEntries.Add(objectStateEntry, objectStateEntry.State);
    						}
     
    						//grab deletes
    						foreach (var objectStateEntry in auditableEntries.Where(e => e.Value == EntityState.Deleted))
    						{
    							Audit audit = GetAuditObject(objectStateEntry.Key, objectStateEntry.Value);
    							if (audit != null) Insert(audit);
     
    						}
     
    						//grab Updates
    						foreach (var objectStateEntry in auditableEntries.Where(e => e.Value == EntityState.Modified))
    						{
    							Audit audit = GetAuditObject(objectStateEntry.Key, objectStateEntry.Value);
    							if (audit != null) Insert(audit);
    						}
     
    						this.DataContext.SaveChanges();
     
    						//grab Inserts
    						foreach (var objectStateEntry in auditableEntries.Where(e => e.Value == EntityState.Added))
    						{
    							Audit audit = GetAuditObject(objectStateEntry.Key, objectStateEntry.Value);
    							if (audit != null) Insert(audit);
    							saveAudit = true;
    						}
     
    						if (saveAudit) this.DataContext.SaveChanges(); //to update the Audit entries
     
    						tran.Complete();
    					}
    				}
    				catch (Exception ex)
    				{
    					logger.LogError(ex);
    					return OperationStatus.CreateFromException("SaveChanges Failed", ex);
    				}
    				return opStatus;
    			}
    		}
    private T GetOriginalEntity<T>(ObjectStateEntry entry, T entity) where T : classIObjectWithChangeTracker
    		{
    			using (ObjectContext context = new ObjectContext(DataContext.Connection.ConnectionString))
    			{
    				ObjectSet<T> objectSet = context.CreateObjectSet<T>();
    				return context.GetObjectByKey(entry.EntityKey) as T;
    			}
    		}

     

    Tuesday, December 6, 2011 6:04 PM
  • This is a terrible answer... it would be useful for ppl looking at the same problem if you expanded a bit on the solution.  What Select?
    Saturday, August 26, 2017 5:00 AM