mhmd
mhmd

Reputation: 1023

How to update not every fields of an object using Entity Framework and EntityState.Modified

I need to update all fields except property1 and property2 for the given entity object.
Having this code:

    [HttpPost]
    public ActionResult Add(object obj)
    {
        if (ModelState.IsValid)
        {
                context.Entry(obj).State = System.Data.EntityState.Modified;

                context.SaveChanges();               
         }
        return View(obj);
    }

How to change it to add an exception to obj.property1 and obj.property2 for not being updated with this code?

Upvotes: 42

Views: 53741

Answers (5)

Slauma
Slauma

Reputation: 177163

You can't define such an exception. You can however mark single properties as modified:

context.Entry(obj).Property(o => o.Property3).IsModified = true;
context.Entry(obj).Property(o => o.Property4).IsModified = true;
// etc.

Note that setting IsModified to false is not supported once you have marked the state of the whole entity to Modified.

For your purpose I would actually prefer to load the entity from the database and then update it using normal change tracking:

var objInDB = context.Objects.Single(o => o.Id == obj.Id);

obj.Property1 = objInDB.Property1;
obj.Property2 = objInDB.Property2;

context.Entry(objInDB).CurrentValues.SetValues(obj);

context.SaveChanges();

Note that only changed properties will be saved by default by Automatic Detect changes. See EF 6 and EF Core articles

Upvotes: 25

Carlos Gregorio
Carlos Gregorio

Reputation: 506

This is an update that works for .net CORE and maybe can help someone who needs a generic solucion and wants to exclude some properties base on different conditions.

I'm using reflection to iterate through the properties and update base on its property value, in this case, as example, i'm excluding the null properties.

    public virtual TEntity Update(TEntity entity)
    {
        dbSet.Attach(entity);
        dbContext.Entry(entity).State = EntityState.Modified;

        var entry = dbContext.Entry(entity);

        Type type = typeof(TEntity);
        PropertyInfo[] properties = type.GetProperties();
        foreach (PropertyInfo property in properties)
        {
            if (property.GetValue(entity, null) == null)
            {
                entry.Property(property.Name).IsModified = false;
            }
        }

        dbContext.SaveChanges();
        return entity;
    }

Upvotes: 1

Boppity Bop
Boppity Bop

Reputation: 10481

The answers above (most of them) use DbContext. For those who is using ObjectContext these solutions arent accessible.

Here is solution for ObjectContext strictly (EF5 .NET 4.5):

ctx.AddObject("ENTITYNAME", item);
ctx.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);

var entry = ctx.ObjectStateManager.GetObjectStateEntry(item);
entry.RejectPropertyChanges("PROPERTY_TO_EXCLUDE");

Upvotes: 0

Arthur Vickers
Arthur Vickers

Reputation: 7533

Let's assume that you have a collection of the properties to be excluded:

var excluded = new[] { "property1", "property2" };

With EF5 on .NET 4.5 you can do this:

var entry = context.Entry(obj);
entry.State = EntityState.Modified;
foreach (var name in excluded)
{
    entry.Property(name).IsModified = false;
}

This uses a new feature of EF5 on .NET 4.5 which allows a property to be set as not modified even after it has been previously set to modified.

When using EF 4.3.1 or EF5 on .NET 4 you can do this instead:

var entry = context.Entry(obj);
foreach (var name in entry.CurrentValues.PropertyNames.Except(excluded))
{
    entry.Property(name).IsModified = true;
}

Upvotes: 78

dyslexicanaboko
dyslexicanaboko

Reputation: 4285

This question was already nicely answered, but I wanted to provide an extension method for anyone who would like to use it.

This code was developed for EF 4.3.1

//You will need to import/use these namespaces    
using System.Data.Entity;
using System.Data.Entity.Infrastructure;    

//Update an entity object's specified columns, comma separated
//This method assumes you already have a context open/initialized
public static void Update<T>(this DbContext context, T entityObject, params string[] properties) where T : class
{
    context.Set<T>().Attach(entityObject);

    var entry = context.Entry(entityObject);

    foreach(string name in properties)
        entry.Property(name).IsModified = true;

    context.SaveChanges();
}

Usage Example

using (FooEntities context = new FooEntities())
{
    FooEntity ef = new FooEntity();

    //For argument's sake say this entity has 4 columns: 
    //    FooID (PK), BarID (FK), Name, Age, CreatedBy, CreatedOn

    //Mock changes
    ef.FooID = 1;
    ef.Name = "Billy";
    ef.Age = 85;

    context.Update<FooEntity>(ef, "Name", "Age"); //I only want to update Name and Age
}

Upvotes: 13

Related Questions