Entity Framework Entry Property to Access Tracking Information for Scalar Properties
Posted on: 2014-06-05
Entity Framework has a change tracker API exposed by the Entry property of the DbContext. The Entry method or the Entries method can be used to fin or to change information that is tracked by Entity Framework. It is possible to know the current value but also the original value. It also possible to track the state of every entity, to read it and to modify it. This article cover the use of Entry for scalar properties. Scalar properties are property that has primitive type like integer, string, datetime, double, etc. It does not include any of your classes. In a subsequent post, we will cover how to track entity inside entity, and also collection of entity.
The entry method has two methods that let you drill down inside an entity to change collection of others entity from an entity or to have access to other reference of entities. From there, we could use the explicit loading method, Load, to load specific portion of information without loading the whole table (as seen in a previous article with DbSet).
This is a simple example to add a new entity with Entry. The code display the Person in the Local memory. The new entry is only set in the Local when its state is changed to Added.
private static void EntryToAddNewEntity() { using (var context = new YourContext()) { Console.WriteLine(context.Persons.Local.Count); //0 var newPerson = new Person {Name = "New Person", BirthDate = new DateTime(1980, 1, 2) }; var entryPerson = context.Entry(newPerson); Console.WriteLine(context.Persons.Local.Count); //0 entryPerson.State = EntityState.Added; Console.WriteLine(context.Persons.Local.Count); //1 context.SaveChanges(); } }
Modifying an existing entity with Entry is also possible without loading this one from the database and without having to update every fields.
private static void EntryToModifyExistingEntityWithoutLoadingFromDatabase() { using (var context = new YourContext()) { Console.WriteLine(context.Persons.Local.Count); //0 var existingPerson = new Person { Id=1, Name = "Updated Name"}; context.Persons.Attach(existingPerson); var entryPerson = context.Entry(existingPerson); Console.WriteLine(context.Persons.Local.Count); //1 entryPerson.Property(d => d.Name).IsModified = true; Console.WriteLine(context.Persons.Local.Count); //1 context.SaveChanges(); } }
It produces a SQL statement with only a single SET.
exec sp_executesql N'UPDATE [dbo].[People] SET [Name] = @0 WHERE ([Id] = @1) ',N'@0 nvarchar(max) ,@1 int',@0=N'Updated Name',@1=1
The advantage of loading from the database is that we do not have to specify which property has been changed.
private static void EntryToModifyExistingEntityByLoadingFromDatabase() { using (var context = new YourContext()) { Console.WriteLine(context.Persons.Local.Count); //0 var existingPerson = context.Persons.Find(1); Console.WriteLine(context.Persons.Local.Count); //1 existingPerson.Name = "Updated from database"; Console.WriteLine(context.Persons.Local.Count); //1 context.SaveChanges(); } }
``` By modifying the property, Entity Framework has inside the tracking the original value and the current value. It knows that the only property changed is the name. When you are developing Client-Server application, you may want to modify only properties that has changed. For example, if you have a web form that has every fields and that the user modify one value, you would want to have an update executed with only the field that has changed to be in the update sql statement. Entity Framework has a method in the Entry property named CurrentValues. CurrentValues lets you set the value you want to be the active one. If this one differ from the the OriginalValues, then it is updated. To simplify the process, the CurrentValues properties has a SetValues method. It takes from an object all scalar properties and map them into the entry. Scalar properties are property that is not another entity or a collection.
```csharp
private static void EntryToModifyByPropertyChanged() { var objectFromUser = new Person {Id = 1, Name="Test", BirthDate = new DateTime(1801, 12, 25)}; using (var context = new YourContext()) { Console.WriteLine(context.Persons.Local.Count); //0 var existingPerson = context.Persons.Find(1); Console.WriteLine(context.Persons.Local.Count); //1 context.Entry(existingPerson).CurrentValues.SetValues(objectFromUser); Console.WriteLine(context.Persons.Local.Count); //1 context.SaveChanges(); } }
This code update Name and BirthDate only if the entity with the ID one has not already in the database this name and birthday. If the name is the same, than only the birthday change. If you execute twice this method, nothing will be updated.
It is also possible to update without doing any select statement from the database by setting to Modified the state of the object that came from the client side.
private static void EntryToModifyByPropertyChangedWithoutUsingFind() { var objectFromUser = new Person { Id = 1, Name = "Tester #2", BirthDate = new DateTime(1941, 12, 25) }; using (var context = new YourContext()) { Console.WriteLine(context.Persons.Local.Count); //0 context.Entry(objectFromUser).State = EntityState.Modified; Console.WriteLine(context.Persons.Local.Count); //1 context.SaveChanges(); } }
So far we have seen how to access the tracking information for all scalar properties with the Entry method. Three others scenarios exist that has not been covered. First, it is about Complex Object. The second is entity that reference an other (optional or required) property. And the third is entity that has a collection of entities. All code in this article is accessible through GitHub or can be downloaded from a Zip file.