Deleting data from the database

Interestingly enough, there is a lot of similarity in the approaches we used for updates and deletions. We can use a query to find data and then mark it for deletion by using the Remove method of DbSet. This approach actually has the same drawbacks as it does with the update, resulting in a select query in addition to the delete query. Nonetheless, let's take a look at how it is done:

using (var context = new Context())
{
    var toDelete = context.People.Find(personId);
    toDelete.Phones.ToList().ForEach(phone => context.Phones.Remove(phone));
    context.People.Remove(toDelete);
    context.SaveChanges();
}

This code deletes each child entity, phone in our case, and then deletes the root entity. You would have to know the primary key value for the entity you want to delete. The preceding code assumes that you have this value in the personId variable. In the case of a web application, this value will be submitted to the method that handles deletion. Alternatively, we could use the RemoveRange method to remove multiple entities in a single statement. Here is how the code looks in VB.NET:

Using context = New Context()
    Dim toDelete = context.People.Find(personId)
    toDelete.Phones.ToList().ForEach(Function(phone) context.Phones.Remove(phone))
    context.People.Remove(toDelete)
    context.SaveChanges()
End Using

There is one important difference between this code and the insert code. We have to manually delete each child record, by removing it from a corresponding collection. Code that is provided relies on lazy loading to work to populate the list of phones for a person. You can also rely on a cascade of delete operation instead, though some DBAs will frown at this practice.

Now, let's delete entities by setting a state on each entity. Again, we need to account for dependent entities. For example:

var toDeleteByState = new Person { PersonId = personId };
toDeleteByState.Phones.Add(new Phone
{
    PhoneId = phoneId1,
    PersonId = personId
});
toDeleteByState.Phones.Add(new Phone
{
    PhoneId = phoneId2,
    PersonId = personId
});

using (var context = new Context())
{
    context.People.Attach(toDeleteByState);
    foreach (var phone in toDeleteByState.Phones.ToList())
    {
        context.Entry(phone).State = EntityState.Deleted;
    }
    context.Entry(toDeleteByState).State = EntityState.Deleted;
    context.SaveChanges();
}

You undoubtedly noticed something very different from any other data manipulation. In order to delete a person, we only need to set the primary key property and nothing else. For phones, we just needed to set the primary key property and parent identifier property. In the case of web applications, you need to submit all the identifiers, or you will need to resort to requerying child data to find the identifiers. Here is how this code looks in VB.NET:

Dim toDeleteByState = New Person With { .PersonId = personId }
toDeleteByState.Phones.Add(New Phone With {.PhoneId = phoneId1, .PersonId = personId })
toDeleteByState.Phones.Add(New Phone With {.PhoneId = phoneId2, .PersonId = personId })
        
Using context = New Context()
    context.People.Attach(toDeleteByState)
    For Each phone In toDeleteByState.Phones.ToList()
        context.Entry(phone).State = EntityState.Deleted
    Next
    context.Entry(toDeleteByState).State = EntityState.Deleted
    context.SaveChanges()
End Using

You can submit full entities for deletion as well. If you are sticking to strict guidelines for deletion for REST web services, those define that only an identifier should be submitted with a web request. So, you will need to decide for yourself which of the two approaches works better for your specific circumstances.

Working with in-memory data

Sometimes, you need the ability to find an entity in an existing context instead of the database. Entity Framework, by default, will always execute queries against the database when you create new context. What if your update involves calling many methods and you want to find what data was added by one of the previous methods? You can force a query to execute only against in-memory data attached to the context using the Local property of DbSet. For instance:

var localQuery = context.People.Local.Where(p => p.LastName.Contains("o")).ToList();

The same code in VB.NET looks as follows:

Dim localQuery = context.People.Local.Where(Function(p) p.LastName.Contains("o")).ToList()

What we also know is that the Find method searches local context first, prior to constructing the database query. You can easily confirm this by forcing another query to load the data you are looking for and then running a Find against one of the found entities. For instance:

var query = context.People.ToList();
var findQuery = context.People.Find(1);

If you run SQL Profiler along with this code, you will see that one query is executed against the database, which confirms that Find runs on in-memory data first. The same code in VB.NET looks as follows:

Dim query = context.People.ToList()
Dim findQuery = context.People.Find(1)

In-memory data also provides access to all the entities with their respective states via the DbChangeTracker object. It allows you to look at the entities and their DbEntityEntry objects as well. For example:

foreach (var dbEntityEntry in context.ChangeTracker.Entries<Person>())
{
    Console.WriteLine(dbEntityEntry.State);
    Console.WriteLine(dbEntityEntry.Entity.LastName);
}

In the preceding code, we get all the person entries that DbContext is tracking as entity entry objects. We can then look at the state of each object as well as an actual Person object that the entity entry belongs to.

Here is the same code in VB.NET:

For Each dbEntityEntry In context.ChangeTracker.Entries(Of Person)
    Console.WriteLine(dbEntityEntry.State)
    Console.WriteLine(dbEntityEntry.Entity.LastName)
Next

This API provides developers with rich capabilities to examine in-memory data at any time.

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

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