Entity Framework - Update Single Property (Partial Update)

Have you ever tried to update specific fields of an entity using Entity Framework? If yes, then I can assume that System.Data.Entity.Validation.DbEntityValidationResult – Validation failed for one or more entities. See ‘EntityValidationErrors’ property for more details. Exception is not something new to you. 

While working recently on a project for which I was using Entity Framework 5.x CF (the part with code first is not relevant) I bumped into an interesting piece of code. The main idea behind it is to perform updates for a single property and ignore the rest even if they don't respect the constraints defined in the model. Please note that I have found PartialEntityValidation class somewhere on the internet and unfortunately I do not remember where, I was not able to find it again. The only changes made by me are related to code formatting. I will present the code first and explain the logic behind it afterwards.

using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;

namespace MyProject.Data.Contexts.Concrete
{
    public class PartialEntityValidation
    {
        private readonly IDictionary<DbEntityEntry, string[]> _store;

        public PartialEntityValidation()
        {
            _store = new Dictionary<DbEntityEntry, string[]>();
        }

        public void Register(DbEntityEntry entry, string[] properties)
        {
            if (_store.ContainsKey(entry))
                _store[entry] = properties;
            else
                _store.Add(entry, properties);
        }

        public void Unregister(DbEntityEntry entry)
        {
            _store.Remove(entry);
        }
        public bool IsResponsibleFor(DbEntityEntry entry)
        {
            return _store.ContainsKey(entry);
        }
        public void Validate(DbEntityValidationResult validationResult)
        {
            var entry = validationResult.Entry;
            foreach (var property in _store[entry])
            {
                var validationErrors = entry.Property(property).GetValidationErrors();
                foreach (var validationError in validationErrors)
                    validationResult.ValidationErrors.Add(validationError);
            }
        }
    }
}
PartialEntityValidation class has a simple constructor which initializes a new dictionary store where the key of it is an DbEntityEntry and the value (currently) an empty string array. The string array will hold later on the properties which should be validated. The  class has four methods: Register, Unregister, IsResponsibleFor and Validate. Now let me explain a bit what each method does. Register adds a new DbEntityEntry with the properties that have to be validated to the existing store. It basically registers a new entity entry for validation. It is quite obvious what Unregister method does; it removes an entity entry from the existing store. IsResponsibleFor method determines if an entity entry is present in the existing store and if it will be part of the validation process. The last method is Validate which accepts a single parameter of DbEntityValidationResult type, from which it takes the entity entry associated with the validation result and validates each property of the entity entry present in the store. If there are any validation errors those are added on ValidationErrors collection of the validation result.

Now it is time to see how to use PartialEntityValidation class. In My case I have exposed it as a public read only property of the context and had ValidateEntity method overridden  at context level.

using System;   
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;

namespace MyProject.Data.Contexts.Concrete
{
    public class MyContext : DbContext
    {
        private readonly PartialEntityValidation _partialEntityValidation;

        static MyContext()
        {
            Database.SetInitializer<T>(null);
        }

        protected MyContext()
            : base("MyDb")
        {
            _partialEntityValidation = new PartialEntityValidation();
            Configuration.LazyLoadingEnabled = false;
        }

        public PartialEntityValidation PartialValidation { get { return _partialEntityValidation; } }

        protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
        {
            if (PartialValidation.IsResponsibleFor(entityEntry))
            {
                var validationResult = new DbEntityValidationResult(entityEntry, new List<DbValidationError>());
                PartialValidation.Validate(validationResult);

                return validationResult;
            }

            return base.ValidateEntity(entityEntry, items);
        }
    }
}
I am sure that I do not have to explain the logic behind ValidateEntity method from the db context. The final piece is the repository where the partial update is  invoked.

using System;
using System.Linq;
using System.Linq.Expressions;

namespace MyProject.Data.Repositories.Concrete
{
    public class MyRepository
    {
		private readonly MyContext _context;
	
        public MyRepository(MyContext context)
        {
			_context = context;
		}

        public void DeleteProduct(Guid productId)
        {
            var product = new Product { ProductId = productId, IsDeleted = true };
            _context.PartialValidation.Register(_context.Entry(product), new[] { "IsDeleted" });
            Update<Product>(workspace, p => p.IsDeleted);
            _context.SaveChanges();
            ((IObjectContextAdapter)_context).ObjectContext.Detach(product);
        }
		
		public void Update<T>(T entity, Expression<Func<T, object>> property) where T : class
        {
            var  entry = _context.Entry(entity);
            _context.Set<T>().Attach(entity);
            entry.Property(property).IsModified = true;
        }
    }
}
Please take into consideration that this is a demo repository which doesn't do much at all except to present  how a partial update is performed. The repository has two important methods. First one is DeleteProduct which performs a soft delete by updating "IsDeleted" property of product entity to true. This is achieved by creating a new product object of which primary key is "ProductId" with "IsDeleted" property set to true. The next step is to register the entity entry along with the "IsDeleted" property (this is the only property we want to have it updated, all other properties of Product entity will be ignored) into PartialValidation store. Update method is called which attaches the entity entry to the context and marks it as modified in order to have it updated by entity framework. That is all, I really hope that you got the idea behind this even if I did not provided an actual sample application for download.

 

 

 

 

Top