Code Like a Pro in C# (9781638356417) by Rodenburg Jort

Code Like a Pro in C# (9781638356417) by Rodenburg Jort

Author:Rodenburg, Jort [Rodenburg, Jort]
Language: eng
Format: epub
Publisher: Simon & Schuster
Published: 2021-07-09T22:00:00+00:00


8.7.4 Querying for pending changes in Entity Framework Core

If we look back at the SaveChangesAsync method in the stub, we see that we access context.Booking before calling base.SaveChangesAsync. Accessing the Booking DbSet before saving any changes we made to the internal DbSets to the database means that there is nothing in the booking collection yet, causing a NullReferenceException, which we catch in the CreateBooking method. The CreateBooking method then throws a CouldNotAddBookingToDatabaseException.

The solution is simple: call base.SaveChangesAsync before accessing context .Booking. Because we are using an in-memory database, we can commit a Booking object to the database during the failure path unit test because the TestInitialize method creates a new instance of the database context (and implicitly wipes the database) before the next test. The important part of the test is that the exception gets thrown. That means we do not need the default case in our switch statement anymore. That being said, let’s change the executed logic on the non-default statement to return an integer of value 1. The SaveChangesAsync method returns (in a nonstubbed scenario) the number of entries written to the database. I see no reason to deviate from that pattern in the stub. We are mimicking its operations, after all.

The only purpose of the nondefault switch value (in this scenario) is to satisfy the required return type of Task<int>. By returning a value of 0, we complete the method and do no harm. We still throw an exception in case CustomerID is anything but 1, as shown in the next listing.

Listing 8.16 Stubbed SaveChangesAsync method with base SaveChangesAsync call

public override async Task<int> SaveChangesAsync(CancellationToken ➥ cancellationToken = default) { await base.SaveChangesAsync(cancellationToken); ❶ return base.Booking.First().CustomerId switch { ❷ 1 => 1, ❸ _ => throw new Exception("Database Error!") ❹ }; }

❶ Calls the nonstub SaveChangesAsync

❷ Switches based on the CustomerId

❸ If the CustomerID is 1, returns a 0

❹ If the CustomerID is not 1, throws an exception

If we run the test now, we see it passes. However, we have an additional problem to consider here. Currently, we’re throwing an exception if the CustomerId is anything but 1, but our changes have already been saved to the database. We really should test the CustomerID before saving to the database. To do this, we need to be able to access the pending changes to the database. Entity Framework Core lets us do this by querying its internal StateTracker for entities with an EntityState of Added as follows:

IEnumerable<EntityEntry> pendingChanges = ➥ ChangeTracker.Entries().Where(e => e.State == EntityState.Added);

The resulting IEnumerable collection contains only our pending changes. What we really want, though, is the Entity objects on the pending changes. We can use a LINQ Select statement to grab only the Entity objects, as shown here:

IEnumerable<object> entities = pendingChanges.Select(e => e.Entity);

From here, we can cast the EntityEntry to our Booking type and grab the CustomerId. To cast (and select only) the entities mapped to a Booking, we can use the OfType<T> method on the IEnumerable as follows:

IEnumerable<Booking> bookings = ➥ pendingChanges.Select(e => e.Entity).OfType<Booking>();

We can



Download



Copyright Disclaimer:
This site does not store any files on its server. We only index and link to content provided by other sites. Please contact the content providers to delete copyright contents if any and email us, we'll remove relevant links or contents immediately.