1 00:00:00,700 --> 00:00:04,890 Our current approach for deleting comic books doesn't handle concurrency. 2 00:00:04,890 --> 00:00:06,420 Let's look at an example. 3 00:00:06,420 --> 00:00:09,570 I already added Bone # 4, so that we could use it for 4 00:00:09,570 --> 00:00:11,420 testing our delete implementation. 5 00:00:14,120 --> 00:00:18,990 I'll open the detail page into another tab, and 6 00:00:18,990 --> 00:00:22,020 then click the delete button to view the delete comic book page. 7 00:00:23,840 --> 00:00:25,802 Back in the first tab, I'll update the comic book. 8 00:00:30,154 --> 00:00:33,670 Then in the second tab, I'll click the delete button. 9 00:00:36,420 --> 00:00:38,830 And the comic book is deleted 10 00:00:38,830 --> 00:00:43,170 without any warning that the entity was updated by another user. 11 00:00:43,170 --> 00:00:48,170 Even worse, if another user had deleted the same comic book before we could, 12 00:00:48,170 --> 00:00:50,720 we would have gotten an unhandled exception. 13 00:00:50,720 --> 00:00:54,350 We can improve this experience by updating the delete comic book page 14 00:00:54,350 --> 00:00:57,210 to take advantage of our concurrency control. 15 00:00:57,210 --> 00:00:59,990 We'll start our refactoring of the delete comic book page 16 00:00:59,990 --> 00:01:03,690 by updating the comic books controller delete action methods, and 17 00:01:03,690 --> 00:01:06,750 their associated view, to use a view model. 18 00:01:06,750 --> 00:01:10,570 As with our other pages that utilize view models, this will give us 19 00:01:10,570 --> 00:01:15,900 a convenient way to pass multiple values from our action methods to the view. 20 00:01:15,900 --> 00:01:18,520 Let's add the view model class to the view models folder. 21 00:01:20,430 --> 00:01:22,819 I'll name the class, ComicBooksDeleteViewModel. 22 00:01:28,460 --> 00:01:29,584 Most of what we need for 23 00:01:29,584 --> 00:01:33,323 this view model already exists in the ComicBook's edit view model class. 24 00:01:38,135 --> 00:01:42,662 The Id property that enables model binding to be able to bind the Id route 25 00:01:42,662 --> 00:01:46,285 parameter value to the ComicBook Id model property, and 26 00:01:46,285 --> 00:01:49,015 the comic book has been deleted property. 27 00:01:53,547 --> 00:01:57,487 And in the ComicBooksBaseViewModel base class, the comic book property. 28 00:02:00,492 --> 00:02:03,506 We could inherit from the ComicBooksBaseViewModel, but 29 00:02:03,506 --> 00:02:07,020 we only need the comic book property for our new view model. 30 00:02:07,020 --> 00:02:08,760 We could so some refactoring and 31 00:02:08,760 --> 00:02:13,000 create a new shared base class, possibly even a generic base class. 32 00:02:13,000 --> 00:02:17,030 But that just feels like it over complicates things too much, 33 00:02:17,030 --> 00:02:18,770 at least at this point in time. 34 00:02:18,770 --> 00:02:22,226 Given that, let's just create a stand alone class, by copying and 35 00:02:22,226 --> 00:02:24,179 pasting what we need into this class. 36 00:02:34,761 --> 00:02:39,621 Now that we have our view model, let's update the comics book controller delete 37 00:02:39,621 --> 00:02:42,350 get and post action methods to make use of it. 38 00:02:43,810 --> 00:02:48,163 For the get action method, we just need to instantiate an instance set the ComicBook 39 00:02:48,163 --> 00:02:51,061 property value, and pass it into the view method call. 40 00:02:57,612 --> 00:03:01,698 For the post action method we need to replace the integer ID parameter 41 00:03:01,698 --> 00:03:05,370 with the ComicBooksDeleteViewModel viewModel parameter. 42 00:03:07,270 --> 00:03:09,950 And in the call to the repositories delete method, 43 00:03:09,950 --> 00:03:14,620 replace the reference to the ID parameter with view model ComicBook.Id. 44 00:03:14,620 --> 00:03:18,114 Then let's wrap the existing method implementation and then try catch block. 45 00:03:23,824 --> 00:03:28,689 And update the cache clause exception type to DbUpdateConcurrencyException, 46 00:03:28,689 --> 00:03:33,280 and add an argument in order to capture a reference to the thrown exception. 47 00:03:35,430 --> 00:03:39,300 Our cache block implementation will be very similar to what we did 48 00:03:39,300 --> 00:03:41,310 in the edit post action method. 49 00:03:41,310 --> 00:03:42,716 So let's grab a copy that code 50 00:03:48,886 --> 00:03:53,185 Actually, the only thing that we need to modify are the error messages that we're 51 00:03:53,185 --> 00:03:58,180 returning to the user, well and fix this reference to the ComicBook entity. 52 00:03:58,180 --> 00:04:02,127 In the first error message, we can change updated to deleted. 53 00:04:04,839 --> 00:04:09,090 The second message will require a more substantial change. 54 00:04:09,090 --> 00:04:12,560 Let's change the message to, the ComicBook being 55 00:04:15,191 --> 00:04:19,090 Deleted has already been updated by another user. 56 00:04:19,090 --> 00:04:24,392 If you still want to not make changes, but if you still want to delete the ComicBook 57 00:04:32,083 --> 00:04:37,570 Then click the Save, change that to Delete button again. 58 00:04:37,570 --> 00:04:41,660 Otherwise, click the Cancel button to not discard your changes. 59 00:04:42,870 --> 00:04:46,025 To return to the detail page. 60 00:04:51,507 --> 00:04:55,850 After we add the model error, we need to return a call to the view method. 61 00:04:55,850 --> 00:04:58,470 Passing in the viewModel instance. 62 00:04:58,470 --> 00:05:02,138 Before we move on to updating the view, let's take a look at the repository method 63 00:05:02,138 --> 00:05:04,598 that we're calling in order to delete the comic book. 64 00:05:08,511 --> 00:05:12,980 The base repositories delete method retrieves the entity from the database in 65 00:05:12,980 --> 00:05:16,160 order to remove it from the identity set. 66 00:05:16,160 --> 00:05:19,660 Under normal circumstances, this would work as expected. 67 00:05:19,660 --> 00:05:23,580 But we need an implementation that will work with our data concurrency control. 68 00:05:23,580 --> 00:05:27,410 The problem is when we retrieve the entity from the database. 69 00:05:27,410 --> 00:05:30,480 We're getting the entities current row version value. 70 00:05:30,480 --> 00:05:34,810 That prevents EF from detecting any updates, or deletes that might have been 71 00:05:34,810 --> 00:05:39,410 performed since the user initially loaded the delete comic book page. 72 00:05:39,410 --> 00:05:43,760 In a moment, we'll see how to ensure that the entities tracking property value 73 00:05:43,760 --> 00:05:47,180 from the initial page load doesn't get lost. 74 00:05:47,180 --> 00:05:51,480 While we're here though, let's add a new delete method to the ComicBookRepository. 75 00:05:51,480 --> 00:05:54,458 That will work correctly with our data concurrency control. 76 00:05:54,458 --> 00:05:58,280 When need to delete method that utilizes a stub entity approach, so 77 00:05:58,280 --> 00:06:02,580 that we can explicitly set the tracking property value that should be used 78 00:06:02,580 --> 00:06:05,410 when EF generates the delete sequel statement. 79 00:06:05,410 --> 00:06:09,110 For now, I'm going to add this method to the ComicBooksRepository. 80 00:06:09,110 --> 00:06:12,240 Though it's certainly possible to push this method down 81 00:06:12,240 --> 00:06:14,100 into the generic base class. 82 00:06:14,100 --> 00:06:17,250 If you'd like to learn more about how to do that, see the teachers notes for 83 00:06:17,250 --> 00:06:18,940 more information. 84 00:06:18,940 --> 00:06:22,920 We want the call word of our delete method to pass in the entities tracking property 85 00:06:22,920 --> 00:06:26,350 value in addition to the entities Id. 86 00:06:26,350 --> 00:06:30,830 For the tracking property value, let's name the parameter row Version, so 87 00:06:30,830 --> 00:06:34,000 that it aligns with the entity's row version property name. 88 00:06:35,400 --> 00:06:38,433 Then let's create our stub comic book entity, setting the Id and 89 00:06:38,433 --> 00:06:41,535 row version properties, to their respective parameter values. 90 00:06:45,012 --> 00:06:49,787 And let's finish up by calling the Context's Entry method, setting the entry 91 00:06:49,787 --> 00:06:54,291 state property to deleted, and calling the Context save changes method. 92 00:06:55,940 --> 00:07:01,020 Now that we have our new data concurrency control friendly delete repository method, 93 00:07:01,020 --> 00:07:03,970 let's call it from the delete post action method. 94 00:07:03,970 --> 00:07:05,280 That's easy to do. 95 00:07:05,280 --> 00:07:07,165 We just need to pass on the view model, 96 00:07:07,165 --> 00:07:11,680 ComicBook.rowVersion property value as the second argument. 97 00:07:11,680 --> 00:07:15,970 Let's move on to our final part of this refactoring effort, updating the view. 98 00:07:18,240 --> 00:07:20,643 Now that we're passing an instance of our view model, 99 00:07:20,643 --> 00:07:24,195 from the controller to the view, instead of an instance of the kind book entity, 100 00:07:24,195 --> 00:07:26,043 we need to update the at model directive. 101 00:07:35,454 --> 00:07:38,526 Before moving on, let's also fix this bad reference. 102 00:07:42,848 --> 00:07:45,288 When the data concurrency exception is thrown, 103 00:07:45,288 --> 00:07:48,858 we're adding models error to the model state in the action method, but 104 00:07:48,858 --> 00:07:53,102 the user won't see those message unless we rendered a validation summary section. 105 00:07:53,102 --> 00:07:56,222 Let's add a call to the validation summary, 106 00:07:56,222 --> 00:08:00,610 HTML helper method right inside the start of our form. 107 00:08:00,610 --> 00:08:03,040 Actually, instead of creating one from scratch, 108 00:08:03,040 --> 00:08:05,138 let's grab a copy of one from the edit view. 109 00:08:10,129 --> 00:08:13,182 Also, when there's a data concurrency error to display, 110 00:08:13,182 --> 00:08:17,090 you want to avoid retrieving the entity from the database again. 111 00:08:17,090 --> 00:08:21,200 Remember, we want the ComicBook's.row Version property as it was from 112 00:08:21,200 --> 00:08:26,110 the initial page load to remain consistent throughout the delete process. 113 00:08:26,110 --> 00:08:29,520 Besides, if the entity was deleted by another user, 114 00:08:29,520 --> 00:08:33,390 it wouldn't even be possible to retrieve the entity again from the database. 115 00:08:33,390 --> 00:08:36,994 To persist the entity's row version property value we can use a hidden field, 116 00:08:36,994 --> 00:08:39,021 just like we did on the edit comic book page. 117 00:08:43,682 --> 00:08:47,098 Unlike the edit ComicBook page, we don't have any text fields for 118 00:08:47,098 --> 00:08:50,020 any of the other comic book entity properties. 119 00:08:50,020 --> 00:08:52,910 Given that, in order to persist the series title and 120 00:08:52,910 --> 00:08:57,190 issue number, we'll need to add hidden fields for those properties as well. 121 00:08:57,190 --> 00:09:01,573 And lastly, let's update the Cancel button's href attribute URL generation. 122 00:09:11,914 --> 00:09:13,980 That completes our refactoring. 123 00:09:13,980 --> 00:09:15,091 Now we're ready to test our changes. 124 00:09:17,906 --> 00:09:20,177 And we got a bad request error, but 125 00:09:20,177 --> 00:09:24,420 that's because we tried to browse directly to the delete view. 126 00:09:27,530 --> 00:09:29,200 Let's navigate to the root of our website. 127 00:09:32,430 --> 00:09:34,249 I'll start by adding Bone # 4, again. 128 00:09:48,351 --> 00:09:51,052 Then I'll open the detail page into another tab 129 00:09:56,461 --> 00:10:02,100 And click the delete button to view the delete ComicBook page. 130 00:10:02,100 --> 00:10:05,053 Back in the first tab, I'll update the ComicBook. 131 00:10:08,993 --> 00:10:12,123 Then in the second tab, I'll click the delete button. 132 00:10:15,296 --> 00:10:19,131 And, we get an error message, letting us know that the Comic Book has been updated. 133 00:10:23,058 --> 00:10:25,309 Back in the first tab, let's delete the Comic Book. 134 00:10:31,531 --> 00:10:34,131 Then in the second tab, I'll click the Delete button again. 135 00:10:38,184 --> 00:10:39,707 And, we get an error message but 136 00:10:39,707 --> 00:10:42,426 in this now that the Comic Book has already been deleted. 137 00:10:44,875 --> 00:10:47,706 Clicking the Cancel button, takes us back to the list page.