Randall Doser
Randall Doser

Reputation: 53

Entity State Always Changed to Modified

I have been using Entity Framework for a few years now. My project is Database First. I recently upgraded to EF 6 from EF 4. I have code generating templates that create partial classes with the following code for scalar properties:

    Private Nullable<double> _ScalarProp;
    public virtual Nullable<double> ScalarProp
    { 
        get
        {
            return _ScalarProp;
        } 
        set
        {
            if(_ScalarProp != value)
            {
                if(IsNotifyEnabled)
                {
                    OnPropertyChanging("ScalarProp");
                    OnScalarPropChanging();
                }
                _ScalarProp = value;
                if(IsNotifyEnabled)
                {
                    OnPropertyChanged("ScalarProp");
                    OnScalarPropChanged();
                }
            }
        }
    }
    partial void OnScalarPropChanging();
    partial void OnScalarPropChanged();

My problem is that when assigning a value to a property that is identical to its current value the above code bypasses the assignment statement HOWEVER the state of the entity goes from Unchanged to Modified. What is happening? Is the Change Tracker operating completely independently of the templated entities? How can I resolve this so the Entity remains Unchanged when the assigned value is identical to the current value?

Upvotes: 1

Views: 1519

Answers (3)

Randall Doser
Randall Doser

Reputation: 53

It seems that the issue is that the ChangeTracker does not attempt to distinguish if the new value differs from the existing value in its overrides of my property setters. Instead it simply marks a property as modified whenever the setter is called. That seems odd to me as I would think it would try to minimize the traffic back to the database by weeding out the non-changes. I would be interested to know if this is the behavior that others are seeing or if it is simply quirk of my particular implementation of EF (translation: I screwed something up). As a workaround I have added the following method to the base class for my entities:

public abstract class EntityBase: INotifyPropertyChanged, INotifyPropertyChanging
{
  ...

  public bool WasUpdated()
  {
    var entry = MyEntities.Context.Entry(this);
    if (entry.State != EntityState.Modified)
      return false;
    bool changed = false;
    foreach (var propName in entry.CurrentValues.PropertyNames)
    {
      changed = changed || (entry.CurrentValues[propName] != entry.OriginalValues[propName]  && !entry.CurrentValues[propName].Equals(entry.OriginalValues[propName]));
      if (changed)
        break;
    }
    return changed;
  }
}

I'll use this until someone is able to help me figure out whether this is the intended behavior of ChangeTracker or I have just screwed up my implementation.

Upvotes: 1

The Vermilion Wizard
The Vermilion Wizard

Reputation: 5395

Is the value being set the result of a calculation? Equality comparison with doubles can have unexpected behavior if those values are the results of calculations. You may want to implement a comparison with tolerance

if (Math.Abs(_ScalarProp.Value - value.Value) > 0.000001)
{  // etc.

(You will need to manually check for null values of your nullable types before using this.)

Upvotes: 1

phil soady
phil soady

Reputation: 11328

Dont have the answer. But I have a tool to find when and why. Add to your context class. You can call it from the immediate window anytime.

myContext.FullDump();

public void FullDump(bool ignoreUnchanged = false)
    {

        Debug.WriteLine("=====Begin of Context Dump=======");
        var dbsetList = this.ChangeTracker.Entries();
        foreach (var dbEntityEntry in dbsetList)
        {
            if (ignoreUnchanged && dbEntityEntry.State == EntityState.Unchanged)
            {
                continue;
            }
            Debug.WriteLine(dbEntityEntry.Entity.GetType().Name + " => " + dbEntityEntry.State);
            switch (dbEntityEntry.State)
            {
                case System.Data.Entity.EntityState.Detached:
                case System.Data.Entity.EntityState.Unchanged:
                case System.Data.Entity.EntityState.Added:
                case System.Data.Entity.EntityState.Modified:
                    WriteCurrentValues(dbEntityEntry);
                    break;
                case System.Data.Entity.EntityState.Deleted:
                    WriteOriginalValues(dbEntityEntry);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            Debug.WriteLine("==========End of Entity======");
        }
        Debug.WriteLine("==========End of Context======");
    }

    private static void WriteCurrentValues(DbEntityEntry dbEntityEntry)
    {
        foreach (var cv in dbEntityEntry.CurrentValues.PropertyNames)
        {
            Debug.WriteLine(cv + "=" + dbEntityEntry.CurrentValues[cv]);
        }
    }
    private static void WriteOriginalValues(DbEntityEntry dbEntityEntry)
    {
        foreach (var cv in dbEntityEntry.OriginalValues.PropertyNames)
        {
            Debug.WriteLine(cv + "=" + dbEntityEntry.OriginalValues[cv]);
        }
    }
}

Upvotes: 0

Related Questions