Entity Framework Copy All Scalar Properties From An Existing Entity
Posted on: 2014-10-07
Entity Framework gives the possibility to create a new instance with properties of an existing entity with a single line instruction. The function that verify every properties and compare them with the one in the context is inside the Entry
function under the name SetValues
.
Let's set up the code to try an update on an existing entity. First, let's create the entity in its own context. This way we insert the entity in the database and kill the context.
//Create a person
using (var context = new YourContext()) {
var person = new Person { Name = "WorkingWithEntry", BirthDate = DateTime.Now };
context.Set<Person>().Add(person);
context.SaveChanges();
}
The second step is to load from the database the person into a variable that will be used in another context. This simulate the scenario of a disconnected entity. It is loaded of values but the next context will not know about it.
using (var context = new YourContext()) { personLoaded = context.Set<Person>().Single(sd => sd.Name == "WorkingWithEntry"); }
Finally, we do a single change or multiple changes to the disconnected entity and call the SetValues
. But before calling this method, we must change at least one property. In the example below, we change the name.
personLoaded.Name = "Modified Name";
using (var context = new YourContext()) {
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ProxyCreationEnabled = false;
var copy = new Person();
context.Entry(copy).State = EntityState.Added;//The context must know the entity to do the copy to.
context.Entry(copy).CurrentValues.SetValues(personLoaded); //The context must also know the entity to copy from.
copy.Name = "This is a copy AKA clone";
context.SaveChanges();
}
After changing the name, we create a new instance of Person and add this new instance to the context by using the Entry method and set the state to Added. We could also use the .Add method which does the same thing. The next step is to use the SetValues
with the entity that contains all changes. Entity Framework will match all properties name and assign values if required. Of course, it is possible to change after calling this method properties to have something unique.
It is not required to use that method if you are modifying an existing entity. Changing the state to Modified do the job. Here is the complete code with the Modified used to an existing entity.
private static void WorkingWithEntryAndAllScalarProperties() {
Person personLoaded;
//Create a person
using (var context = new YourContext()) {
var person = new Person { Name = "WorkingWithEntry", BirthDate = DateTime.Now }; context.Set<Person>().Add(person);
context.SaveChanges();
}
//Load the person
using (var context = new YourContext()) {
personLoaded = context.Set<Person>().Single(sd => sd.Name == "WorkingWithEntry");
}
personLoaded.Name = "Modified Name";
//Save the person (modified its properties)
using (var context = new YourContext()) {
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ProxyCreationEnabled = false;
context.Entry(personLoaded).State = EntityState.Modified;
context.SaveChanges(); var copy = new Person();
context.Entry(copy).State = EntityState.Added;//The context must know the entity to do the copy to.
context.Entry(copy).CurrentValues.SetValues(personLoaded); //The context must also know the entity to copy from.
copy.Name = "This is a copy AKA clone";
context.SaveChanges();
}
}
The SQL generated by this code for the SaveChanges() after the EntityState.Modified is a huge one with all properties updated, even those that has not changed.
exec sp_executesql N'UPDATE [dbo].[People] SET [Name] = @0, [BirthDate] = @1 WHERE ([Id] = @2) ',N'@0 nvarchar(max) ,@1 datetime2(7),@2 int',@0=N'Modified Name',@1='2014-07-31 12:00:05.9600000',@2=12
If you want to update only specific fields in the table you have to set the property to modified instead of the whole entity like the code below.
private static void UpdatingASpecificFieldOfATable() {
Person person;
//Create a person
using (var context = new YourContext()) {
person = new Person { Name = "Update only a single property", BirthDate = DateTime.Now };
context.Set<Person>().Add(person);
context.SaveChanges();
}
//Update a single field
using (var context = new YourContext()) {
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ProxyCreationEnabled = false;
context.Entry(person).State = EntityState.Unchanged; //In the context without any modification
person.BirthDate = new DateTime(1984, 08, 01);
person.Name = "This will not be saved in the database";
context.Entry(person).Property(d => d.BirthDate).IsModified = true; //context.Entry(person).Property(d => d.Name).IsModified = false;
context.SaveChanges();
}
}
It is important to see that I have commented the line that set to IsModified to False the name. We do not need to specify properties that is not modified if we have AutoDetectChangesEnabled to false. In the case that it is to true, you need to set it manually to false because Entity Framework detects a changes when setting the propreties. The goal here is to modify the Name and the BirthDate property but only allow the update of the BirthDate. Here is a screenshot of the database.
We are over the subject of SetValues with the Entry and Modified property. However, it is interesting to see that you can have a lot of control about what you want to be modified or not with Entity Framework. You can see the code of this article on GitHub or download a zip file with the source code.