17.3. Working with Data Stores

With our data store and persistent class configured, we are ready to realize the full power of ORM.

Note

This chapter has no syntax changes. You should be able to follow along with your own code or the CodingEventsDemo projects.

17.3.1. Data Stores in the Controller - Video

Since our data store, EventDbContext, extends DbContext, we have access to all of the methods defined by DbContext. There are quite a few such methods (see the documentation for details), but we will only use SaveChanges.

We will make more extensive use of the Events property of EventDbContext, which is of type DbSet. As mentioned in the previous section, this property allows us to query for objects directly from the database. It too has quite a few methods, and we will demonstrate usage of Add, Remove, ToList, and Find.

17.3.2. Data Stores in the Controller - Text

17.3.2.1. Adding the Data Store

To access data from the database in a controller, we’ll need an instance of EventDbContext. Adding the following code to the top of EventsController will make one available as an instance variable of the controller.

17
18
19
20
21
22
   private EventDbContext context;

   public EventsController(EventDbContext dbContext)
   {
      context = dbContext;
   }

We can now reference context anywhere within our controller in order to query the database.

Note

It may not be obvious how this code works, since we never explicitly call the constructor for EventsController. When we run our application, ASP.NET will call this constructor for us, and will pass in an instance of EventDbContext. This is an example of the concept of dependency injection, a topic which is fairly complex and beyond the scope of this course.

17.3.2.2. Using Data Store Methods

Let’s refactor our controller to replace each usage of the in-memory store EventData with our instance of EventDbContext.

The first such usage is in the Index method, which displays a listing of all of the events. We are currently calling EventData.GetAll(). To carry out the equivalent action on context, we can use its Events property. Modify Index to look like the code below.

25
26
27
28
29
30
   public IActionResult Index()
   {
      List<Event> events = context.Events.ToList();

      return View(events);
   }

We use the ToList method of context.Events (recall that this property is a DbSet) to fetch a list of all Event objects stored in the database.

Our next usage of EventData that needs to be replaced is in the Add method that handles POST requests. That method is currently calling EventData.Add(newEvent) to store a newly created Event object. Replace this line with the following.

53
54
   context.Events.Add(newEvent);
   context.SaveChanges();

Line 53 adds the object to context.Events, which only stores it within that DbSet object. For this change to be pushed to the database, we must also call context.SaveChanges().

Note

An instance of a persistent class, such as Event, that has not been stored in the database is called a transient object.

The next usage of EventData is in the Delete method that handles GET requests. As in Index, this method is calling EventData.GetAll(), which we can replace with context.Events.ToList().

62
63
64
65
66
67
   public IActionResult Delete()
   {
      ViewBag.events = context.Events.ToList();

      return View();
   }

The final usage of EventData is in the Delete method that handles POST requests. That method currently looks like this:

61
62
63
64
65
66
67
68
69
70
   [HttpPost]
   public IActionResult Delete(int[] eventIds)
   {
      foreach (int eventId in eventIds)
      {
         EventData.Remove(eventId);
      }

      return Redirect("/Events");
   }

The method takes in an array of IDs corresponding to objects that should be deleted. It then loops through the array and deletes the corresponding objects one-by-one.

Line 66 can be replaced with the following:

66
67
   Event theEvent = context.Events.Find(eventId);
   context.Events.Remove(theEvent);

The first line searches context.Events for an object with the given ID using its Find method. It returns the given object or null (if none is found). We can then delete the object by calling the Remove method of context.Events and passing in the object we want to delete.

Since we have changed the state of context, we must also call context.SaveChanges() to make sure these changes get reflected in the database. However, we need only do this after the loop. Since each call to SaveChanges results in a database query, this makes our code much more efficient than calling SaveChanges within the loop. Instead of several database queries, we have only one.

Our final refactored method looks like this:

70
71
72
73
74
75
76
77
78
79
80
81
82
   [HttpPost]
   public IActionResult Delete(int[] eventIds)
   {
      foreach (int eventId in eventIds)
      {
            Event theEvent = context.Events.Find(eventId);
            context.Events.Remove(theEvent);
      }

      context.SaveChanges();

      return Redirect("/Events");
   }

Now that we are no longer using EventData, we can delete it from our application. And as always, be sure to start your app and test after refactoring.

Tip

Remember that any time you update your database, you need to add a migration to your project and update your database.

If you do not, you will not see any of the changes you made.

17.3.3. Check Your Understanding

Question

True/False: The only methods available for querying objects within a DbSet are Add, Remove, ToList, and Find.

Question

Which DbContext method must be called in order to push changes to the database?

  1. Save
  2. SaveAll
  3. PushChanges
  4. SaveChanges