Examining the Details and Delete methods
Open the Movie controller and examine the Details
method:
[!code-csharpMain]
1: //#define snippet_1stSearch
2: //#define snippet_SearchID
3: #define snippet_SearchGenre
4:
5:
6: using Microsoft.AspNetCore.Mvc;
7: using Microsoft.AspNetCore.Mvc.Rendering;
8: using Microsoft.EntityFrameworkCore;
9: using MvcMovie.Models;
10: using System;
11: using System.Linq;
12: using System.Threading.Tasks;
13:
14: namespace MvcMovie.Controllers
15: {
16: public class MoviesController : Controller
17: {
18: private readonly MvcMovieContext _context;
19:
20: public MoviesController(MvcMovieContext context)
21: {
22: _context = context;
23: }
24:
25: #region snippet_details
26: // GET: Movies/Details/5
27: public async Task<IActionResult> Details(int? id)
28: {
29: if (id == null)
30: {
31: return NotFound();
32: }
33:
34: var movie = await _context.Movie
35: .SingleOrDefaultAsync(m => m.ID == id);
36: if (movie == null)
37: {
38: return NotFound();
39: }
40:
41: return View(movie);
42: }
43: #endregion
44: #region snippetCreate
45: // GET: Movies/Create
46: public IActionResult Create()
47: {
48: return View();
49: }
50:
51: // POST: Movies/Create
52: [HttpPost]
53: [ValidateAntiForgeryToken]
54: public async Task<IActionResult> Create(
55: [Bind("ID,Title,ReleaseDate,Genre,Price, Rating")] Movie movie)
56: {
57: if (ModelState.IsValid)
58: {
59: _context.Add(movie);
60: await _context.SaveChangesAsync();
61: return RedirectToAction("Index");
62: }
63: return View(movie);
64: }
65: #endregion
66:
67: // GET: Movies/Edit/5
68: public async Task<IActionResult> Edit(int? id)
69: {
70: if (id == null)
71: {
72: return NotFound();
73: }
74:
75: var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
76: if (movie == null)
77: {
78: return NotFound();
79: }
80: return View(movie);
81: }
82:
83: // POST: Movies/Edit/5
84: // To protect from overposting attacks, please enable the specific properties you want to bind to, for
85: // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
86: [HttpPost]
87: [ValidateAntiForgeryToken]
88: public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
89: {
90: if (id != movie.ID)
91: {
92: return NotFound();
93: }
94:
95: if (ModelState.IsValid)
96: {
97: try
98: {
99: _context.Update(movie);
100: await _context.SaveChangesAsync();
101: }
102: catch (DbUpdateConcurrencyException)
103: {
104: if (!MovieExists(movie.ID))
105: {
106: return NotFound();
107: }
108: else
109: {
110: throw;
111: }
112: }
113: return RedirectToAction("Index");
114: }
115: return View(movie);
116: }
117:
118: #region snippet_delete
119: #region snippet_delete2
120: // GET: Movies/Delete/5
121: public async Task<IActionResult> Delete(int? id)
122: {
123: #endregion
124: if (id == null)
125: {
126: return NotFound();
127: }
128:
129: var movie = await _context.Movie
130: .SingleOrDefaultAsync(m => m.ID == id);
131: if (movie == null)
132: {
133: return NotFound();
134: }
135:
136: return View(movie);
137: }
138:
139: #region snippet_delete3
140: // POST: Movies/Delete/5
141: [HttpPost, ActionName("Delete")]
142: [ValidateAntiForgeryToken]
143: public async Task<IActionResult> DeleteConfirmed(int id)
144: {
145: #endregion
146: var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
147: _context.Movie.Remove(movie);
148: await _context.SaveChangesAsync();
149: return RedirectToAction("Index");
150: }
151: #endregion
152:
153: private bool MovieExists(int id)
154: {
155: return _context.Movie.Any(e => e.ID == id);
156: }
157: #if snippet_1stSearch
158: // First Search
159: #region snippet_1stSearch
160: public async Task<IActionResult> Index(string searchString)
161: {
162: var movies = from m in _context.Movie
163: select m;
164:
165: if (!String.IsNullOrEmpty(searchString))
166: {
167: movies = movies.Where(s => s.Title.Contains(searchString));
168: }
169:
170: return View(await movies.ToListAsync());
171: }
172: #endregion
173: // End first Search
174: #endif
175:
176: #if snippet_SearchID
177: // Search ID
178: #region snippet_SearchID
179: public async Task<IActionResult> Index(string id)
180: {
181: var movies = from m in _context.Movie
182: select m;
183:
184: #region snippet_SearchNull
185: if (!String.IsNullOrEmpty(id))
186: {
187: movies = movies.Where(s => s.Title.Contains(id));
188: }
189: #endregion
190:
191: return View(await movies.ToListAsync());
192: }
193: #endregion
194: // End search ID
195: #endif
196:
197: #if SearchPost
198: // Search Post
199: #region snippet_SearchPost
200: [HttpPost]
201: public string Index(string searchString, bool notUsed)
202: {
203: return "From [HttpPost]Index: filter on " + searchString;
204: }
205: #endregion
206: // End SP
207: #endif
208:
209: #if snippet_SearchGenre
210: // Search by genre.
211: #region snippet_SearchGenre
212: // Requires using Microsoft.AspNetCore.Mvc.Rendering;
213: public async Task<IActionResult> Index(string movieGenre, string searchString)
214: {
215: #region snippet_LINQ
216: // Use LINQ to get list of genres.
217: IQueryable<string> genreQuery = from m in _context.Movie
218: orderby m.Genre
219: select m.Genre;
220: #endregion
221:
222: var movies = from m in _context.Movie
223: select m;
224:
225: #region snippet_SearchNull2
226: if (!String.IsNullOrEmpty(searchString))
227: {
228: movies = movies.Where(s => s.Title.Contains(searchString));
229: }
230: #endregion
231:
232: if (!String.IsNullOrEmpty(movieGenre))
233: {
234: movies = movies.Where(x => x.Genre == movieGenre);
235: }
236:
237: var movieGenreVM = new MovieGenreViewModel();
238: movieGenreVM.genres = new SelectList(await genreQuery.Distinct().ToListAsync());
239: movieGenreVM.movies = await movies.ToListAsync();
240:
241: return View(movieGenreVM);
242: }
243: #endregion
244: #endif
245: }
246: }
The MVC scaffolding engine that created this action method adds a comment showing an HTTP request that invokes the method. In this case it???s a GET request with three URL segments, the Movies
controller, the Details
method and an id
value. Recall these segments are defined in Startup.cs.
[!code-csharpMain]
1: #if V1x
2: using System;
3: using System.Collections.Generic;
4: using System.Linq;
5: using System.Threading.Tasks;
6: using Microsoft.AspNetCore.Builder;
7: using Microsoft.AspNetCore.Hosting;
8: using Microsoft.Extensions.Configuration;
9: using Microsoft.Extensions.DependencyInjection;
10: using Microsoft.Extensions.Logging;
11: using Microsoft.EntityFrameworkCore;
12: using MvcMovie.Models;
13:
14: namespace MvcMovie
15: {
16: public class Startup
17: {
18: public Startup(IHostingEnvironment env)
19: {
20: var builder = new ConfigurationBuilder()
21: .SetBasePath(env.ContentRootPath)
22: .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
23: .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
24: .AddEnvironmentVariables();
25: Configuration = builder.Build();
26: }
27:
28: public IConfigurationRoot Configuration { get; }
29:
30: // This method gets called by the runtime. Use this method to add services to the container.
31: #region ConfigureServices
32: public void ConfigureServices(IServiceCollection services)
33: {
34: // Add framework services.
35: services.AddMvc();
36:
37: services.AddDbContext<MvcMovieContext>(options =>
38: options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
39: }
40: #endregion
41:
42:
43: // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
44: public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
45: {
46: loggerFactory.AddConsole(Configuration.GetSection("Logging"));
47: loggerFactory.AddDebug();
48:
49: if (env.IsDevelopment())
50: {
51: app.UseDeveloperExceptionPage();
52:
53: // Browser Link is not compatible with Kestrel 1.1.0
54: // For details on enabling Browser Link, see https://go.microsoft.com/fwlink/?linkid=840936
55: // app.UseBrowserLink();
56: }
57: else
58: {
59: app.UseExceptionHandler("/Home/Error");
60: }
61:
62: #region snippet_seed
63: app.UseStaticFiles();
64: #region snippet_1
65: app.UseMvc(routes =>
66: {
67: routes.MapRoute(
68: name: "default",
69: template: "{controller=Home}/{action=Index}/{id?}");
70: });
71: #endregion
72:
73: SeedData.Initialize(app.ApplicationServices);
74: }
75: }
76: }
77: #endregion
78: #endif
EF makes it easy to search for data using the SingleOrDefaultAsync
method. An important security feature built into the method is that the code verifies that the search method has found a movie before it tries to do anything with it. For example, a hacker could introduce errors into the site by changing the URL created by the links from http://localhost:xxxx/Movies/Details/1
to something like http://localhost:xxxx/Movies/Details/12345
(or some other value that doesn???t represent an actual movie). If you did not check for a null movie, the app would throw an exception.
Examine the Delete
and DeleteConfirmed
methods.
[!code-csharpMain]
1: //#define snippet_1stSearch
2: //#define snippet_SearchID
3: #define snippet_SearchGenre
4:
5:
6: using Microsoft.AspNetCore.Mvc;
7: using Microsoft.AspNetCore.Mvc.Rendering;
8: using Microsoft.EntityFrameworkCore;
9: using MvcMovie.Models;
10: using System;
11: using System.Linq;
12: using System.Threading.Tasks;
13:
14: namespace MvcMovie.Controllers
15: {
16: public class MoviesController : Controller
17: {
18: private readonly MvcMovieContext _context;
19:
20: public MoviesController(MvcMovieContext context)
21: {
22: _context = context;
23: }
24:
25: #region snippet_details
26: // GET: Movies/Details/5
27: public async Task<IActionResult> Details(int? id)
28: {
29: if (id == null)
30: {
31: return NotFound();
32: }
33:
34: var movie = await _context.Movie
35: .SingleOrDefaultAsync(m => m.ID == id);
36: if (movie == null)
37: {
38: return NotFound();
39: }
40:
41: return View(movie);
42: }
43: #endregion
44: #region snippetCreate
45: // GET: Movies/Create
46: public IActionResult Create()
47: {
48: return View();
49: }
50:
51: // POST: Movies/Create
52: [HttpPost]
53: [ValidateAntiForgeryToken]
54: public async Task<IActionResult> Create(
55: [Bind("ID,Title,ReleaseDate,Genre,Price, Rating")] Movie movie)
56: {
57: if (ModelState.IsValid)
58: {
59: _context.Add(movie);
60: await _context.SaveChangesAsync();
61: return RedirectToAction("Index");
62: }
63: return View(movie);
64: }
65: #endregion
66:
67: // GET: Movies/Edit/5
68: public async Task<IActionResult> Edit(int? id)
69: {
70: if (id == null)
71: {
72: return NotFound();
73: }
74:
75: var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
76: if (movie == null)
77: {
78: return NotFound();
79: }
80: return View(movie);
81: }
82:
83: // POST: Movies/Edit/5
84: // To protect from overposting attacks, please enable the specific properties you want to bind to, for
85: // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
86: [HttpPost]
87: [ValidateAntiForgeryToken]
88: public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
89: {
90: if (id != movie.ID)
91: {
92: return NotFound();
93: }
94:
95: if (ModelState.IsValid)
96: {
97: try
98: {
99: _context.Update(movie);
100: await _context.SaveChangesAsync();
101: }
102: catch (DbUpdateConcurrencyException)
103: {
104: if (!MovieExists(movie.ID))
105: {
106: return NotFound();
107: }
108: else
109: {
110: throw;
111: }
112: }
113: return RedirectToAction("Index");
114: }
115: return View(movie);
116: }
117:
118: #region snippet_delete
119: #region snippet_delete2
120: // GET: Movies/Delete/5
121: public async Task<IActionResult> Delete(int? id)
122: {
123: #endregion
124: if (id == null)
125: {
126: return NotFound();
127: }
128:
129: var movie = await _context.Movie
130: .SingleOrDefaultAsync(m => m.ID == id);
131: if (movie == null)
132: {
133: return NotFound();
134: }
135:
136: return View(movie);
137: }
138:
139: #region snippet_delete3
140: // POST: Movies/Delete/5
141: [HttpPost, ActionName("Delete")]
142: [ValidateAntiForgeryToken]
143: public async Task<IActionResult> DeleteConfirmed(int id)
144: {
145: #endregion
146: var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
147: _context.Movie.Remove(movie);
148: await _context.SaveChangesAsync();
149: return RedirectToAction("Index");
150: }
151: #endregion
152:
153: private bool MovieExists(int id)
154: {
155: return _context.Movie.Any(e => e.ID == id);
156: }
157: #if snippet_1stSearch
158: // First Search
159: #region snippet_1stSearch
160: public async Task<IActionResult> Index(string searchString)
161: {
162: var movies = from m in _context.Movie
163: select m;
164:
165: if (!String.IsNullOrEmpty(searchString))
166: {
167: movies = movies.Where(s => s.Title.Contains(searchString));
168: }
169:
170: return View(await movies.ToListAsync());
171: }
172: #endregion
173: // End first Search
174: #endif
175:
176: #if snippet_SearchID
177: // Search ID
178: #region snippet_SearchID
179: public async Task<IActionResult> Index(string id)
180: {
181: var movies = from m in _context.Movie
182: select m;
183:
184: #region snippet_SearchNull
185: if (!String.IsNullOrEmpty(id))
186: {
187: movies = movies.Where(s => s.Title.Contains(id));
188: }
189: #endregion
190:
191: return View(await movies.ToListAsync());
192: }
193: #endregion
194: // End search ID
195: #endif
196:
197: #if SearchPost
198: // Search Post
199: #region snippet_SearchPost
200: [HttpPost]
201: public string Index(string searchString, bool notUsed)
202: {
203: return "From [HttpPost]Index: filter on " + searchString;
204: }
205: #endregion
206: // End SP
207: #endif
208:
209: #if snippet_SearchGenre
210: // Search by genre.
211: #region snippet_SearchGenre
212: // Requires using Microsoft.AspNetCore.Mvc.Rendering;
213: public async Task<IActionResult> Index(string movieGenre, string searchString)
214: {
215: #region snippet_LINQ
216: // Use LINQ to get list of genres.
217: IQueryable<string> genreQuery = from m in _context.Movie
218: orderby m.Genre
219: select m.Genre;
220: #endregion
221:
222: var movies = from m in _context.Movie
223: select m;
224:
225: #region snippet_SearchNull2
226: if (!String.IsNullOrEmpty(searchString))
227: {
228: movies = movies.Where(s => s.Title.Contains(searchString));
229: }
230: #endregion
231:
232: if (!String.IsNullOrEmpty(movieGenre))
233: {
234: movies = movies.Where(x => x.Genre == movieGenre);
235: }
236:
237: var movieGenreVM = new MovieGenreViewModel();
238: movieGenreVM.genres = new SelectList(await genreQuery.Distinct().ToListAsync());
239: movieGenreVM.movies = await movies.ToListAsync();
240:
241: return View(movieGenreVM);
242: }
243: #endregion
244: #endif
245: }
246: }
Note that the HTTP GET Delete
method doesn???t delete the specified movie, it returns a view of the movie where you can submit (HttpPost) the deletion. Performing a delete operation in response to a GET request (or for that matter, performing an edit operation, create operation, or any other operation that changes data) opens up a security hole.
The [HttpPost]
method that deletes the data is named DeleteConfirmed
to give the HTTP POST method a unique signature or name. The two method signatures are shown below:
[!code-csharpMain]
1: //#define snippet_1stSearch
2: //#define snippet_SearchID
3: #define snippet_SearchGenre
4:
5:
6: using Microsoft.AspNetCore.Mvc;
7: using Microsoft.AspNetCore.Mvc.Rendering;
8: using Microsoft.EntityFrameworkCore;
9: using MvcMovie.Models;
10: using System;
11: using System.Linq;
12: using System.Threading.Tasks;
13:
14: namespace MvcMovie.Controllers
15: {
16: public class MoviesController : Controller
17: {
18: private readonly MvcMovieContext _context;
19:
20: public MoviesController(MvcMovieContext context)
21: {
22: _context = context;
23: }
24:
25: #region snippet_details
26: // GET: Movies/Details/5
27: public async Task<IActionResult> Details(int? id)
28: {
29: if (id == null)
30: {
31: return NotFound();
32: }
33:
34: var movie = await _context.Movie
35: .SingleOrDefaultAsync(m => m.ID == id);
36: if (movie == null)
37: {
38: return NotFound();
39: }
40:
41: return View(movie);
42: }
43: #endregion
44: #region snippetCreate
45: // GET: Movies/Create
46: public IActionResult Create()
47: {
48: return View();
49: }
50:
51: // POST: Movies/Create
52: [HttpPost]
53: [ValidateAntiForgeryToken]
54: public async Task<IActionResult> Create(
55: [Bind("ID,Title,ReleaseDate,Genre,Price, Rating")] Movie movie)
56: {
57: if (ModelState.IsValid)
58: {
59: _context.Add(movie);
60: await _context.SaveChangesAsync();
61: return RedirectToAction("Index");
62: }
63: return View(movie);
64: }
65: #endregion
66:
67: // GET: Movies/Edit/5
68: public async Task<IActionResult> Edit(int? id)
69: {
70: if (id == null)
71: {
72: return NotFound();
73: }
74:
75: var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
76: if (movie == null)
77: {
78: return NotFound();
79: }
80: return View(movie);
81: }
82:
83: // POST: Movies/Edit/5
84: // To protect from overposting attacks, please enable the specific properties you want to bind to, for
85: // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
86: [HttpPost]
87: [ValidateAntiForgeryToken]
88: public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
89: {
90: if (id != movie.ID)
91: {
92: return NotFound();
93: }
94:
95: if (ModelState.IsValid)
96: {
97: try
98: {
99: _context.Update(movie);
100: await _context.SaveChangesAsync();
101: }
102: catch (DbUpdateConcurrencyException)
103: {
104: if (!MovieExists(movie.ID))
105: {
106: return NotFound();
107: }
108: else
109: {
110: throw;
111: }
112: }
113: return RedirectToAction("Index");
114: }
115: return View(movie);
116: }
117:
118: #region snippet_delete
119: #region snippet_delete2
120: // GET: Movies/Delete/5
121: public async Task<IActionResult> Delete(int? id)
122: {
123: #endregion
124: if (id == null)
125: {
126: return NotFound();
127: }
128:
129: var movie = await _context.Movie
130: .SingleOrDefaultAsync(m => m.ID == id);
131: if (movie == null)
132: {
133: return NotFound();
134: }
135:
136: return View(movie);
137: }
138:
139: #region snippet_delete3
140: // POST: Movies/Delete/5
141: [HttpPost, ActionName("Delete")]
142: [ValidateAntiForgeryToken]
143: public async Task<IActionResult> DeleteConfirmed(int id)
144: {
145: #endregion
146: var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
147: _context.Movie.Remove(movie);
148: await _context.SaveChangesAsync();
149: return RedirectToAction("Index");
150: }
151: #endregion
152:
153: private bool MovieExists(int id)
154: {
155: return _context.Movie.Any(e => e.ID == id);
156: }
157: #if snippet_1stSearch
158: // First Search
159: #region snippet_1stSearch
160: public async Task<IActionResult> Index(string searchString)
161: {
162: var movies = from m in _context.Movie
163: select m;
164:
165: if (!String.IsNullOrEmpty(searchString))
166: {
167: movies = movies.Where(s => s.Title.Contains(searchString));
168: }
169:
170: return View(await movies.ToListAsync());
171: }
172: #endregion
173: // End first Search
174: #endif
175:
176: #if snippet_SearchID
177: // Search ID
178: #region snippet_SearchID
179: public async Task<IActionResult> Index(string id)
180: {
181: var movies = from m in _context.Movie
182: select m;
183:
184: #region snippet_SearchNull
185: if (!String.IsNullOrEmpty(id))
186: {
187: movies = movies.Where(s => s.Title.Contains(id));
188: }
189: #endregion
190:
191: return View(await movies.ToListAsync());
192: }
193: #endregion
194: // End search ID
195: #endif
196:
197: #if SearchPost
198: // Search Post
199: #region snippet_SearchPost
200: [HttpPost]
201: public string Index(string searchString, bool notUsed)
202: {
203: return "From [HttpPost]Index: filter on " + searchString;
204: }
205: #endregion
206: // End SP
207: #endif
208:
209: #if snippet_SearchGenre
210: // Search by genre.
211: #region snippet_SearchGenre
212: // Requires using Microsoft.AspNetCore.Mvc.Rendering;
213: public async Task<IActionResult> Index(string movieGenre, string searchString)
214: {
215: #region snippet_LINQ
216: // Use LINQ to get list of genres.
217: IQueryable<string> genreQuery = from m in _context.Movie
218: orderby m.Genre
219: select m.Genre;
220: #endregion
221:
222: var movies = from m in _context.Movie
223: select m;
224:
225: #region snippet_SearchNull2
226: if (!String.IsNullOrEmpty(searchString))
227: {
228: movies = movies.Where(s => s.Title.Contains(searchString));
229: }
230: #endregion
231:
232: if (!String.IsNullOrEmpty(movieGenre))
233: {
234: movies = movies.Where(x => x.Genre == movieGenre);
235: }
236:
237: var movieGenreVM = new MovieGenreViewModel();
238: movieGenreVM.genres = new SelectList(await genreQuery.Distinct().ToListAsync());
239: movieGenreVM.movies = await movies.ToListAsync();
240:
241: return View(movieGenreVM);
242: }
243: #endregion
244: #endif
245: }
246: }
[!code-csharpMain]
1: //#define snippet_1stSearch
2: //#define snippet_SearchID
3: #define snippet_SearchGenre
4:
5:
6: using Microsoft.AspNetCore.Mvc;
7: using Microsoft.AspNetCore.Mvc.Rendering;
8: using Microsoft.EntityFrameworkCore;
9: using MvcMovie.Models;
10: using System;
11: using System.Linq;
12: using System.Threading.Tasks;
13:
14: namespace MvcMovie.Controllers
15: {
16: public class MoviesController : Controller
17: {
18: private readonly MvcMovieContext _context;
19:
20: public MoviesController(MvcMovieContext context)
21: {
22: _context = context;
23: }
24:
25: #region snippet_details
26: // GET: Movies/Details/5
27: public async Task<IActionResult> Details(int? id)
28: {
29: if (id == null)
30: {
31: return NotFound();
32: }
33:
34: var movie = await _context.Movie
35: .SingleOrDefaultAsync(m => m.ID == id);
36: if (movie == null)
37: {
38: return NotFound();
39: }
40:
41: return View(movie);
42: }
43: #endregion
44: #region snippetCreate
45: // GET: Movies/Create
46: public IActionResult Create()
47: {
48: return View();
49: }
50:
51: // POST: Movies/Create
52: [HttpPost]
53: [ValidateAntiForgeryToken]
54: public async Task<IActionResult> Create(
55: [Bind("ID,Title,ReleaseDate,Genre,Price, Rating")] Movie movie)
56: {
57: if (ModelState.IsValid)
58: {
59: _context.Add(movie);
60: await _context.SaveChangesAsync();
61: return RedirectToAction("Index");
62: }
63: return View(movie);
64: }
65: #endregion
66:
67: // GET: Movies/Edit/5
68: public async Task<IActionResult> Edit(int? id)
69: {
70: if (id == null)
71: {
72: return NotFound();
73: }
74:
75: var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
76: if (movie == null)
77: {
78: return NotFound();
79: }
80: return View(movie);
81: }
82:
83: // POST: Movies/Edit/5
84: // To protect from overposting attacks, please enable the specific properties you want to bind to, for
85: // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
86: [HttpPost]
87: [ValidateAntiForgeryToken]
88: public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
89: {
90: if (id != movie.ID)
91: {
92: return NotFound();
93: }
94:
95: if (ModelState.IsValid)
96: {
97: try
98: {
99: _context.Update(movie);
100: await _context.SaveChangesAsync();
101: }
102: catch (DbUpdateConcurrencyException)
103: {
104: if (!MovieExists(movie.ID))
105: {
106: return NotFound();
107: }
108: else
109: {
110: throw;
111: }
112: }
113: return RedirectToAction("Index");
114: }
115: return View(movie);
116: }
117:
118: #region snippet_delete
119: #region snippet_delete2
120: // GET: Movies/Delete/5
121: public async Task<IActionResult> Delete(int? id)
122: {
123: #endregion
124: if (id == null)
125: {
126: return NotFound();
127: }
128:
129: var movie = await _context.Movie
130: .SingleOrDefaultAsync(m => m.ID == id);
131: if (movie == null)
132: {
133: return NotFound();
134: }
135:
136: return View(movie);
137: }
138:
139: #region snippet_delete3
140: // POST: Movies/Delete/5
141: [HttpPost, ActionName("Delete")]
142: [ValidateAntiForgeryToken]
143: public async Task<IActionResult> DeleteConfirmed(int id)
144: {
145: #endregion
146: var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
147: _context.Movie.Remove(movie);
148: await _context.SaveChangesAsync();
149: return RedirectToAction("Index");
150: }
151: #endregion
152:
153: private bool MovieExists(int id)
154: {
155: return _context.Movie.Any(e => e.ID == id);
156: }
157: #if snippet_1stSearch
158: // First Search
159: #region snippet_1stSearch
160: public async Task<IActionResult> Index(string searchString)
161: {
162: var movies = from m in _context.Movie
163: select m;
164:
165: if (!String.IsNullOrEmpty(searchString))
166: {
167: movies = movies.Where(s => s.Title.Contains(searchString));
168: }
169:
170: return View(await movies.ToListAsync());
171: }
172: #endregion
173: // End first Search
174: #endif
175:
176: #if snippet_SearchID
177: // Search ID
178: #region snippet_SearchID
179: public async Task<IActionResult> Index(string id)
180: {
181: var movies = from m in _context.Movie
182: select m;
183:
184: #region snippet_SearchNull
185: if (!String.IsNullOrEmpty(id))
186: {
187: movies = movies.Where(s => s.Title.Contains(id));
188: }
189: #endregion
190:
191: return View(await movies.ToListAsync());
192: }
193: #endregion
194: // End search ID
195: #endif
196:
197: #if SearchPost
198: // Search Post
199: #region snippet_SearchPost
200: [HttpPost]
201: public string Index(string searchString, bool notUsed)
202: {
203: return "From [HttpPost]Index: filter on " + searchString;
204: }
205: #endregion
206: // End SP
207: #endif
208:
209: #if snippet_SearchGenre
210: // Search by genre.
211: #region snippet_SearchGenre
212: // Requires using Microsoft.AspNetCore.Mvc.Rendering;
213: public async Task<IActionResult> Index(string movieGenre, string searchString)
214: {
215: #region snippet_LINQ
216: // Use LINQ to get list of genres.
217: IQueryable<string> genreQuery = from m in _context.Movie
218: orderby m.Genre
219: select m.Genre;
220: #endregion
221:
222: var movies = from m in _context.Movie
223: select m;
224:
225: #region snippet_SearchNull2
226: if (!String.IsNullOrEmpty(searchString))
227: {
228: movies = movies.Where(s => s.Title.Contains(searchString));
229: }
230: #endregion
231:
232: if (!String.IsNullOrEmpty(movieGenre))
233: {
234: movies = movies.Where(x => x.Genre == movieGenre);
235: }
236:
237: var movieGenreVM = new MovieGenreViewModel();
238: movieGenreVM.genres = new SelectList(await genreQuery.Distinct().ToListAsync());
239: movieGenreVM.movies = await movies.ToListAsync();
240:
241: return View(movieGenreVM);
242: }
243: #endregion
244: #endif
245: }
246: }
The common language runtime (CLR) requires overloaded methods to have a unique parameter signature (same method name but different list of parameters). However, here you need two Delete
methods ??? one for GET and one for POST ??? that both have the same parameter signature. (They both need to accept a single integer as a parameter.)
There are two approaches to this problem, one is to give the methods different names. That???s what the scaffolding mechanism did in the preceding example. However, this introduces a small problem: ASP.NET maps segments of a URL to action methods by name, and if you rename a method, routing normally wouldn???t be able to find that method. The solution is what you see in the example, which is to add the ActionName("Delete")
attribute to the DeleteConfirmed
method. That attribute performs mapping for the routing system so that a URL that includes /Delete/ for a POST request will find the DeleteConfirmed
method.
Another common work around for methods that have identical names and signatures is to artificially change the signature of the POST method to include an extra (unused) parameter. That???s what we did in a previous post when we added the notUsed
parameter. You could do the same thing here for the [HttpPost] Delete
method:
// POST: Movies/Delete/6
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete(int id, bool notUsed)
Publish to Azure
See (xref:)Publish an ASP.NET Core web app to Azure App Service using Visual Studio for instructions on how to publish this app to Azure using Visual Studio. The app can also be published from the (xref:)command line.
Thanks for completing this introduction to ASP.NET Core MVC. We appreciate any comments you leave. (xref:)Getting started with MVC and EF Core is an excellent follow up to this tutorial.
|