سؤال

I'm using EntityFramework 5 running on .NET 4.5

I'm trying to perform a record update, using approach as below:

var rec = DbCntx.MyRecords.Attach(changedRecord);
var entry = DbCntx.Entry(changedRecord);
if (entry.State == EntityState.Modified) DbCntx.SaveChanges();

The passed in data object changedRecord contained with at least 1 field value modified, compare to the actual db. However the Entry.State is always Unchanged, so SaveChanges() never triggered.

I'm writing this update routine to support any data changes over the changedRecord, the data changes is totally depend on the client/caller, so I shouldn't specifically change any field value in this routine.

My problem using this approach, I can't get the original data values. All the entity I can accessed are the exact of changedRecord, which is as a disconnected record with changes I haven't commit into the db.

Without the original data, I can't identify which properties are changes. If I try to use Find(), First/SingleOrDefault() to locate the record 1st, I can't use Attach() later as it would cause error "ObjectStateManager cannot track multiple objects with the same key".

from Googling, some hinted to use Detach(), but Detach is not available in DbContext.

هل كانت مفيدة؟

المحلول

This is typically a case where you'd want to use AddOrUpdate (or this for version 6):

using System.Data.Entity.Migrations;
....

DbCntx.MyRecords.AddOrUpdate(changedRecord);
DbCntx.SaveChanges();

This marks all changed properties as modified. By default, the entity is fetched from the database by its key value(s). You can tell AddOrUpdate to use an alternate key, if necessary.

نصائح أخرى

AddOrUpdate() proposed by Gert does worked. However, I need to capture the differences in codes. At the end, this is what I worked out, using DbPropertyValues:

            dbCntx.MyRec.Attach(chgRec);

            var entry = dbCntx.Entry(chgRec);

            var dbVals = entry.GetDatabaseValues();
            var dbValsPs = dbVals.PropertyNames;

            foreach (var p in dbValsPs)
            {
                var ep = entry.Property(p);
                var cv = ep.CurrentValue;
                var dbv = dbVals.GetValue<object>(p);

                if (!MyHelper.AreValuesEqual(cv, dbv))
                    ep.IsModified = true;
            }

            if (entry.State == EntityState.Modified)
                affectedCount = dbCntx.SaveChanges();

    public static bool AreValuesEqual<T>(T valueA, T valueB)
    {
        // array handling (such as byte[])
        if (valueA != null)
        {
            if (valueB == null) return false;

            Type t = valueA.GetType();
            if (t.IsArray)
            {
                IEnumerable<object> listA = (valueA as Array).Cast<object>();
                IEnumerable<object> listB = (valueB as Array).Cast<object>();
                return listA.SequenceEqual(listB);
            }
        }

        return EqualityComparer<T>.Default.Equals(valueA, valueB);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top