Patrick Desjardins Blog
Patrick Desjardins picture from a conference

How to update specific field of your entity with a generic method and Entity Framework

Posted on: 2013-04-16

If you are using Entity Framework with a repository layer which has the basic method like Get, Update, Insert (or Add) and Delete you are already in a good position. But what if you want to update a single property without having to load the whole entity from the database? If you have only the primary key (id) of your entity and the value of the property you want to update, you may want to simply update the field with a where clause with the id. In SQL, we would create the following query.

 update [dbo].[WorkoutSessions] 
 set [Name] = 'New Name', 
 [Order] = 1 
 where ([Id] = 123) 

With Entity Framework, you could create single method like "UpdateNameAndOrder" but this would end to having a lot of update method if you require to have partial update among many properties. A better approach would be to specify which properties we want to update.

 public int Update(T entity, Expression<Func<T, object>>[] properties) { 
   DatabaseContext.Entry(entity).State = EntityState.Unchanged; 
   foreach (var property in properties) { 
     var propertyName = ExpressionHelper.GetExpressionText(property); 
     DatabaseContext.Entry(entity).Property(propertyName).IsModified = true; 
   } 
   return DatabaseContext.SaveChangesWithoutValidation(); 
} 

The code above is in the BaseRepository class of the project described in the Enterprise project. As you can see, it takes as its second parameter an expression of a function. This will let use this method by specifying in a Lambda expression witch property to update.

 ...Update(Model, d=>d.Name); //or ...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn); 

As you can see, the Update method first line change the state to Unchanged. We could have using Attach of the IDbSet but since in the Enterprise project we have special sets a way to simply go directly to the DbSet of the current database context is to go directly to the Entry. Next, we loop all properties chosen. Inside the loop, we are using a System.Web.Mvc namespace. We could have using the code of GetExpressionText without having to use this namespace. Here is the code if you do not want a reference to this dll.

 /// <summary> 
 /// Gets the model name from a lambda expression. 
 /// </summary> 
 /// 
 /// <returns> 
 /// The model name. 
 /// </returns> 
 /// <param name="expression">The expression.</param> 
 public static string GetExpressionText(LambdaExpression expression) { 
  Stack<string> stack = new Stack<string>(); 
  Expression expression1 = expression.Body; 
  while (expression1 != null) { 
    if (expression1.NodeType == ExpressionType.Call) { 
      MethodCallExpression methodCallExpression = (MethodCallExpression) expression1; 
    if (ExpressionHelper.IsSingleArgumentIndexer((Expression) methodCallExpression)) { 
      stack.Push(ExpressionHelper.GetIndexerInvocation(Enumerable.Single<Expression>((IEnumerable<Expression>) methodCallExpression.Arguments), Enumerable.ToArray<ParameterExpression>((IEnumerable<ParameterExpression>) expression.Parameters))); 
      expression1 = methodCallExpression.Object; 
    } else 
    break; 
    } else if (expression1.NodeType == ExpressionType.ArrayIndex) { 
      BinaryExpression binaryExpression = (BinaryExpression) expression1; 
      stack.Push(ExpressionHelper.GetIndexerInvocation(binaryExpression.Right, Enumerable.ToArray<ParameterExpression>((IEnumerable<ParameterExpression>) expression.Parameters))); 
      expression1 = binaryExpression.Left; 
    } else if (expression1.NodeType == ExpressionType.MemberAccess) { 
      MemberExpression memberExpression = (MemberExpression) expression1; 
      stack.Push("." + memberExpression.Member.Name); 
      expression1 = memberExpression.Expression; 
    } else if (expression1.NodeType == ExpressionType.Parameter) { 
      stack.Push(string.Empty); expression1 = (Expression) null; 
    } else 
      break; 
    } 
    if (stack.Count > 0 && string.Equals(stack.Peek(), ".model", StringComparison.OrdinalIgnoreCase)) stack.Pop(); 
    if (stack.Count <= 0) return string.Empty; 
    return Enumerable.Aggregate<string>((IEnumerable<string>) stack, (Func<string, string, string>) ((left, right) => left + right)).TrimStart(new char[1] { '.' }); 
  } 

What it does is it takes from the lambda the name of the property selected.

The last thing the update method is doing it's saving without executing the validation on the entity. This is required since not the whole entity is loaded. Some property required might not be loaded which would result into validation exception. To be honest, an improvement would be to execute the validation for the updated property, but for the sake of simplicity, we will stay this smaller.

To conclude, it's possible to update an entity in a generic way without having to preload this object or to update all its properties.