Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attach entity in modified state without marking all properties dirty

I'm trying to figure out how to mark specific properties of a detached entity as modified. If I do the following, it will mark all properties modified and the generated sql will update all columns.

/// <summary>
/// Sets the entity in the modified state.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity">The entity.</param>
void IDbContext.Modified<T>(T entity)
{
    DbEntityEntry<T> entry = Entry(entity);
    if (entry.State == EntityState.Modified)
    {
        // if the state is already Modified we don't need to do anything else
        return;
    }

    if (entry.State == EntityState.Detached)
    {
        Set<T>().Attach(entity);

        //TODO: set specific properties modified instead of the the whole object.
        entry.State = EntityState.Modified;
    }
}

How do I set only changed properties as Modified?

I'm trying to use this in a class that implements DbContext that will be used by a generic repository. The goal is to automagically determine which properties have changed compared to the database values and then set those changed properties states to Modified. In my current implementation, the Modified method has no knowledge of the entity type, so I can't simply retrieve it with context.Set<T>.Find(key).

I suppose I could add an overload that accepts an originalEntity parameter, but I'd rather not if possible.

void IDbContext.Modified<T>(T entity, T originalEntity)
{
    DbEntityEntry<T> entry = Entry(entity);
    if (entry.State == EntityState.Modified)
    {
        // if the state is already Modified we don't need to do anything else
        return;
    }

    if (entry.State == EntityState.Detached)
    {
        Set<T>().Attach(entity);

        entry.OriginalValues.SetValues(originalEntity);
    }
}
like image 728
jrummell Avatar asked Jul 26 '11 12:07

jrummell


People also ask

How does DbContext change state of entity?

This can be achieved in several ways: setting the EntityState for the entity explicitly; using the DbContext. Update method (which is new in EF Core); using the DbContext. Attach method and then "walking the object graph" to set the state of individual properties within the graph explicitly.

What does EntityState Modified do?

State = EntityState. Modified; , you are not only attaching the entity to the DbContext , you are also marking the whole entity as dirty. This means that when you do context. SaveChanges() , EF will generate an update statement that will update all the fields of the entity.

What is attach in Entity Framework?

Attach is used to repopulate a context with an entity that is known to already exist in the database. SaveChanges will therefore not attempt to insert an attached entity into the database because it is assumed to already be there.

What is EntityState detached?

Detached. 1. The object exists but is not being tracked. An entity is in this state immediately after it has been created and before it is added to the object context.


1 Answers

You must do it for each property manually by calling:

DbEntityEntry<T> entry = context.Entry(entity);
if (entry.State == EntityState.Detached)
{
    context.Set<T>().Attach(entity);
    entry.Property(e => e.SomeProperty).IsModified = true;
    // TODO other properties
}

Edit:

Former example suppose that you don't want to reload the entity from the database and in such case you must know which properties are changed - it is up to you to implement it.

If you are happy with additional query to database you can use this:

var persistedEntity = context.Set<T>.Find(key);
var entry = context.Entry(persistedEntity);
entry.CurrentValues.SetValues(entity);

Edit2:

Setting original values should be reverse operation (but I have never tried this):

var persistedEntity = context.Set<T>.Find(key);
context.Entry(persistedEntity).State = EntityState.Detached;

var entry = context.Entry(entity);
context.Set<T>.Attach(entity);
// I'm not sure if you have to change the state of the entity
entry.OriginalValues.SetValues(persistedEntity);
like image 94
Ladislav Mrnka Avatar answered Oct 02 '22 19:10

Ladislav Mrnka