Updating data in the database

What does it mean to update data in the database? We want to replace one or more column values in a table's row with new values. Entity Framework will issue an update query when it knows that an entity has changed since it was first attached to DbContext, either by viewing a LINQ query that was enumerated, or via a call to Attach method of DbSet. The simplest way to find an entity you want to update is to use a query. Then, change one or more properties to new values and call SaveChanges. From the moment we query the data, Entity Framework will start tracking changes to each property. When SaveChanges is finally called, only changed properties will be included in the update SQL operation. When you want to find an entity to update in the database, you typically look for it based on the primary key value. We already saw how to use the Where method to achieve this. Entity Framework also has the Find method exposed on DbSet. This method takes one or more values as parameters that correspond to the primary key of the table mapped to that DbSet. We use the column that has a unique ID as the primary key in our example, hence we only need a single value. If you use composite primary keys, consisting of more than one column, which is typical for junction tables, you will need to pass the values for each column that the primary key is comprised of in the exact order of the primary key columns. If you are following along with this exercise, open SSMS or the SQL Server Object Explorer window inside Visual Studio and find the ID of a person in the People table to practice on. In the following code, the value passed to the Find function is 1:

using (var context = new Context())
{
    var person = context.People.Find(1);
    person.FirstName = "New Name";
    context.SaveChanges();
}

The same code in VB.NET looks as follows:

Using context = New Context()
    Dim person = context.People.Find(1)
    person.FirstName = "New Name"
    context.SaveChanges()
End Using

If you trap the SQL query sent to the SQL Server when SaveChanges is called, it will look as follows:

UPDATE [dbo].[People]
SET [FirstName] = @0
WHERE ([PersonId] = @1)

This proves that indeed only the changes that are made explicitly are sent back to the database. For example, the last name was not updated, since it was not changed. If you look in SQL Profiler for the entire code block, you will see that the Find method also resulted in a query that is shown in the following code snippet:

SELECT TOP (2)
    [Extent1].[PersonId] AS [PersonId],
    [Extent1].[PersonTypeId] AS [PersonTypeId],
    [Extent1].[FirstName] AS [FirstName],
    [Extent1].[LastName] AS [LastName],
    [Extent1].[MiddleName] AS [MiddleName],
    [Extent1].[BirthDate] AS [BirthDate],
    [Extent1].[HeightInFeet] AS [HeightInFeet],
    [Extent1].[IsActive] AS [IsActive]
    FROM [dbo].[People] AS [Extent1]
    WHERE [Extent1].[PersonId] = @p0

Find was translated into the SingleOrDefault method call. That is why we selected the Top (2) rows. We want to make sure that there is only one entity that matches the primary key.

If you are writing a desktop Windows application, you may choose to use the approach of keeping the context around after a query is fired to issue updates. The entity must remain connected to the context from a query to the SaveChanges call timeline. You can find an entity, let the user make changes, and finally call SaveChanges. If you want to model this approach in our code, user interactions correspond to the person.FirstName = "New Name" line of code. If you are working on a web application, this approach does not work. You cannot keep the original context around or between two web server calls. You do not really need to take the overhead of finding an entity twice, once to show to the user and the second time to update. Instead, let's use the second approach from the insert examples and set the state, as shown in the following code snippet:

var person2 = new Person
{
    PersonId = 1,
    BirthDate = new DateTime(1980, 1, 2),
    FirstName = "Jonathan",
    HeightInFeet = 6.1m,
    IsActive = true,
    LastName = "Smith",
    MiddleName = "M"
};
person2.Phones.Add(new Phone
{
    PhoneNumber = "updated 1",
    PhoneId = 1,
    PersonId = 1
});
person2.Phones.Add(new Phone
{
    PhoneNumber = "updated 2",
    PhoneId = 2,
    PersonId = 1
});
using (var context = new Context())
{
    context.Entry(person2).State = EntityState.Modified;
    context.SaveChanges();
}

You can imagine that the data we created initially, prior to instantiating the context, was submitted via a web call to our Web API controller. Then, once inside the controller, we create the context, set the state, and save changes. If you look at the results of this code inside the database, you might be surprised to find out that the person data was updated, but the phone data was not. This occurred because of a fundamental difference between the insert and update implementation inside Entity Framework. When you set the state to modified, Entity Framework does not propagate this change to the entire object graph. So, to make this code work properly, we need to add a little bit more code, as shown in the following code snippet:

using (var context = new Context())
{
    context.Entry(person2).State = EntityState.Modified;
    foreach (var phone in person2.Phones)
    {
        context.Entry(phone).State = EntityState.Modified;
    }
    context.SaveChanges();
}

All we had to do manually was set the state of each changed entity. Of course, if you have a new phone number in the collection, you can set its state to Added instead of Modified. There is one more important concept contained within the code. Whenever we use the state change approach, we must know all the columns' data, including the primary key for each entity. This is because Entity Framework assumes that when the state is changed, all the properties need to be updated. Here is how the code looks in VB.NET:

Dim person2 = New Person() With {
    .PersonId = 1,
    .BirthDate = New DateTime(1980, 1, 2),
    .FirstName = "Jonathan",
    .HeightInFeet = 6.1D,
    .IsActive = True,
    .LastName = "Smith",
    .MiddleName = "M"
}
person2.Phones.Add(New Phone() With {.PhoneNumber = "updated 1", .PhoneId = 1, .PersonId = 1})
person2.Phones.Add(New Phone() With {.PhoneNumber = "updated 2", .PhoneId = 2, .PersonId = 1})
Using context = New Context()
    context.Entry(person2).State = EntityState.Modified
    For Each phone In person2.Phones
        context.Entry(phone).State = EntityState.Modified
    Next
    context.SaveChanges()
End Using

If you capture SQL queries sent when this code is run, you will see three update queries—one for the person and one more for each of the two phone numbers.

It is also worth repeating that Entity Framework tracks the state of the entities once they are attached to the context. So, if you query the data, the context starts tracking your entities. If you are writing a web application, this tracking becomes an unnecessary overhead for query operations. The reason it is unnecessary is because you will dispose of the content, destroying the tracking as soon as the web request to get the data completes. Entity Framework has a way to reduce this overhead. For example:

using (var context = new Context())
{
    var query = context.People.Include(p => p.Phones).AsNoTracking();
    foreach (var person in query)
    {
        foreach (var phone in person.Phones)
        {
        }
    }
}

If you put a breakpoint inside the loop and check the entity state, using context.Entry(person).State and context.Entry(phone).State expressions, you will see that the state is Detached for both entities. This means that this entity is not tracked by the context, thus reducing your overhead by using the AsNoTracking method.

The same code in VB.NET looks as follows:

Using context = New Context()
    Dim query = From person In context.People.Include(Function(p) p.Phones).AsNoTracking()
                Select person
    For Each person As Person In query
        For Each phone As Phone In person.Phones
        Next
    Next
End Using

We also combined turning off change tracking with eager loading. What if even in web environments you only want to update just the properties that are changed by a user? One big assumption is that you will have to track what is changed in your web application on the client. Assuming that this is accomplished, you can use yet another approach to accomplish the update operation. You can use the Attach method on DbSet. This method essentially sets the state to Unchanged and context starts tracking the entity in question. After you attach an entity, you can just set one of the changed properties at a time. You must know in advance which properties have changed. For example:

var person3 = new Person
{
    PersonId = 1,
    BirthDate = new DateTime(1980, 1, 2),
    FirstName = "Jonathan",
    HeightInFeet = 6.1m,
    IsActive = true,
    LastName = "Smith",
    MiddleName = "M"
};
using (var context = new Context())
{
    context.People.Attach(person3);
    person3.LastName = "Updated";
    context.SaveChanges();
}

This code will result in a query that only updates the LastName column and nothing else. The same code in VB.NET looks as follows:

Dim person3 = New Person() With {
    .PersonId = 1,
    .BirthDate = New DateTime(1980, 1, 2),
    .FirstName = "Jonathan",
    .HeightInFeet = 6.1D,
    .IsActive = True,
    .LastName = "Smith",
    .MiddleName = "M"
}
Using context = New Context()
    context.People.Attach(person3)
    person3.LastName = "Updated"
    context.SaveChanges()
End Using

Alternatively, instead of calling the Attach method, you can simply set the state by calling context.Entry(person3).State = EntityState.Unchanged. Just replace one line of code that calls Attach with this line and you are done.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.50.87