I have Two different ASP.net Core 6.0 applications running that have tables linked to other tables by foreign keys. I create a CRUD for both pages. At the first CRUD (PriceList) page, I will enter the data. At the the second CRUD (CombineStage) of the page I will get the data (Stage) from the first page (Pricelist) using multiple select and the search box and its corresponding price in the Index page after using multiple select. I can't find any code commands to make this work. Any help would be appreciated. My foreign key is PricelistId. Below is my code:
CombineStage.cs
using System.ComponentModel.DataAnnotations;
namespace WebApp.Models
{
public class CombineStage
{
[Key]
public int StageId { get; set; }
[Required(ErrorMessage = "Bạn chưa nhập tên công đoạn")]
public string Name { get; set; }
[Required(ErrorMessage = "Bạn chưa chọn công đoạn")]
public int PricelistId { get; set; }
[Required(ErrorMessage = "Bạn chưa chọn ảnh")]
public string Picture { get; set; }
[Required(ErrorMessage = "Bạn chưa nhập giá tiền")]
public int Price { get; set; }
public virtual PriceList PriceList { get; set; }
}
}
CombineStagesController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using WebApp.Models;
namespace WebApp.Areas.Admin.Controllers
{
[Area("Admin")]
public class CombineStagesController : Controller
{
private readonly ManageContext _context;
public CombineStagesController(ManageContext context)
{
_context = context;
}
// GET: Admin/CombineStages
public async Task<IActionResult> Index()
{
var manageContext = _context.CombineStage.Include(c => c.PriceList);
return View(await manageContext.ToListAsync());
}
// GET: Admin/CombineStages/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null || _context.CombineStage == null)
{
return NotFound();
}
var combineStage = await _context.CombineStage
.Include(c => c.PriceList)
.FirstOrDefaultAsync(m => m.StageId == id);
if (combineStage == null)
{
return NotFound();
}
return View(combineStage);
}
// GET: Admin/CombineStages/Create
public IActionResult Create()
{
ViewData["PricelistId"] = new SelectList(_context.PriceLists, "PricelistId", "Stage");
return View();
}
// POST: Admin/CombineStages/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("StageId,Name,PricelistId,Picture,Price")] CombineStage combineStage)
{
if (ModelState.IsValid)
{
_context.Add(combineStage);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["PricelistId"] = new SelectList(_context.PriceLists, "PricelistId", "Stage", combineStage.PricelistId);
return View(combineStage);
}
// GET: Admin/CombineStages/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null || _context.CombineStage == null)
{
return NotFound();
}
var combineStage = await _context.CombineStage.FindAsync(id);
if (combineStage == null)
{
return NotFound();
}
ViewData["PricelistId"] = new SelectList(_context.PriceLists, "PricelistId", "Stage", combineStage.PricelistId);
return View(combineStage);
}
// POST: Admin/CombineStages/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("StageId,Name,PricelistId,Picture,Price")] CombineStage combineStage)
{
if (id != combineStage.StageId)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(combineStage);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CombineStageExists(combineStage.StageId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["PricelistId"] = new SelectList(_context.PriceLists, "PricelistId", "Stage", combineStage.PricelistId);
return View(combineStage);
}
// GET: Admin/CombineStages/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null || _context.CombineStage == null)
{
return NotFound();
}
var combineStage = await _context.CombineStage
.Include(c => c.PriceList)
.FirstOrDefaultAsync(m => m.StageId == id);
if (combineStage == null)
{
return NotFound();
}
return View(combineStage);
}
// POST: Admin/CombineStages/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
if (_context.CombineStage == null)
{
return Problem("Entity set 'ManageContext.CombineStage' is null.");
}
var combineStage = await _context.CombineStage.FindAsync(id);
if (combineStage != null)
{
_context.CombineStage.Remove(combineStage);
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool CombineStageExists(int id)
{
return _context.CombineStage.Any(e => e.StageId == id);
}
}
}
Create
#model WebApp.Models.CombineStage
#{
ViewData["Title"] = "Create";
Layout = "~/Areas/Admin/Views/Shared/_AdminLayout.cshtml";
}
<h1>Create</h1>
<h4>CombineStage</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PricelistId" class="control-label"></label>
<select asp-for="PricelistId" class ="form-control" asp-items="ViewBag.PricelistId" multiple>
</select>
</div>
<div class="form-group">
<label asp-for="Picture" class="control-label"></label>
<input asp-for="Picture" class="form-control" />
<span asp-validation-for="Picture" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
Thank you so much!
Update Pricelist:
Pricelist.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Drawing;
namespace WebApp.Models
{
public partial class PriceList
{
public PriceList()
{
StageDetails = new HashSet<StageDetail>();
CombineStages = new HashSet<CombineStage>();
}
[Key]
public int PricelistId { get; set; }
[Required(ErrorMessage = "Bạn chưa chọn ảnh")]
public string Image { get; set; }
[Required (ErrorMessage = "Bạn chưa chọn máy may")]
public Machine Machine { get; set; }
[Required (ErrorMessage = "Bạn chưa tên nhập công đoạn")]
public string Stage { get; set; }
[Required (ErrorMessage = "Bạn chưa nhập giá tiền")]
public int Price { get; set; }
public virtual ICollection<StageDetail> StageDetails { get; set; }
public virtual ICollection<CombineStage> CombineStages { get; set; }
}
public enum Machine
{
VS,
[Display(Name = "1K")]
OneK
}
}
PriceListsController
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using WebApp.Models;
using WebApp.ViewModel;
namespace WebApp.Areas.Admin.Controllers
{
[Area("Admin")]
public class PriceListsController : Controller
{
private readonly ManageContext _context;
private readonly IWebHostEnvironment _environment;
public PriceListsController(ManageContext context, IWebHostEnvironment environment)
{
_context = context;
_environment = environment;
}
public async Task<IActionResult> Index()
{
return View(await _context.PriceLists.ToListAsync());
}
public async Task<IActionResult> Details(int? id)
{
try
{
if (id == null)
{
return NotFound();
}
var priceList = await _context.PriceLists
.FirstOrDefaultAsync(m => m.PricelistId == id);
var priceListModelView = new PriceListViewModel()
{
Id = priceList.PricelistId,
ExistingImage = priceList.Image,
Machine = priceList.Machine,
Stage = priceList.Stage,
Price = priceList.Price
};
if (priceList == null)
{
return NotFound();
}
return View(priceList);
}
catch (Exception)
{
throw;
}
}
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(PriceListViewModel model)
{
try
{
if (ModelState.IsValid)
{
string uniqueFileName = ProcessUploadedFile(model);
PriceList priceList = new()
{
Image = uniqueFileName,
Machine = model.Machine,
Stage = model.Stage,
Price = model.Price
};
_context.Add(priceList);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
catch (Exception)
{
throw;
}
return View(model);
}
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var priceList = await _context.PriceLists.FindAsync(id);
var priceListViewModel = new PriceListViewModel()
{
Id = priceList.PricelistId,
ExistingImage = priceList.Image,
Machine = priceList.Machine,
Stage = priceList.Stage,
Price = priceList.Price
};
if (priceList == null)
{
return NotFound();
}
return View(priceListViewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, PriceListViewModel model)
{
if (ModelState.IsValid)
{
var priceList = await _context.PriceLists.FindAsync(model.Id);
priceList.Machine = model.Machine;
priceList.Stage = model.Stage;
priceList.Price = model.Price;
if (model.PricelistImage != null)
{
if (model.ExistingImage != null)
{
string filePath = Path.Combine(_environment.WebRootPath, "Images", model.ExistingImage);
System.IO.File.Delete(filePath);
}
priceList.Image = ProcessUploadedFile(model);
}
_context.Update(priceList);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View();
}
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var priceList = await _context.PriceLists
.FirstOrDefaultAsync(m => m.PricelistId == id);
var priceListViewModel = new PriceListViewModel()
{
Id = priceList.PricelistId,
ExistingImage = priceList.Image,
Machine = priceList.Machine,
Stage = priceList.Stage,
Price = priceList.Price
};
if (priceList == null)
{
return NotFound();
}
return View(priceListViewModel);
}
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var priceList = await _context.PriceLists.FindAsync(id);
//string deleteFileFromFolder = "wwwroot\\Uploads\\";
string deleteFileFromFolder = Path.Combine(_environment.WebRootPath, "Images");
var CurrentImage = Path.Combine(Directory.GetCurrentDirectory(), deleteFileFromFolder, priceList.Image);
_context.PriceLists.Remove(priceList);
if (System.IO.File.Exists(CurrentImage))
{
System.IO.File.Delete(CurrentImage);
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool SpeakerExists(int id)
{
return _context.PriceLists.Any(e => e.PricelistId == id);
}
private string ProcessUploadedFile(PriceListViewModel model)
{
string uniqueFileName = null;
string path = Path.Combine(_environment.WebRootPath, "Images");
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
if (model.PricelistImage != null)
{
string uploadsFolder = Path.Combine(_environment.WebRootPath, "Images");
uniqueFileName = Guid.NewGuid().ToString() + "_" + model.PricelistImage.FileName;
string filePath = Path.Combine(uploadsFolder, uniqueFileName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
model.PricelistImage.CopyTo(fileStream);
}
}
return uniqueFileName;
}
}
}
Update Create(HTTPPost) CombineStagesController:
(But it still hasn't solved my request. I want when I select multiple then I get the entire corresponding Price value in Index Page)
public IActionResult Create()
{
ViewData["PricelistId"] = new SelectList(_context.PriceLists, "PricelistId", "Stage");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("StageId,Name,PricelistId,Picture,Price")] CombineStage combineStage, int[] PricelistId)
{
foreach (var id in PricelistId)
{
var stage = _context.PriceLists.Where(m => m.PricelistId == id).Select(x => x.Stage).ToString();
}
if (ModelState.IsValid)
{
_context.Add(combineStage);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(combineStage);
}
Update
Things I'm looking forward to
Next I will choose 2 option stages that I created
Next step, I want the total price of the 2 process options I have selected here
Finally, I want to show all the stages and prices of my 2 options that I have selected
That's all my request.
It's not entirely clear what you're expected result is with regard to records and their respective prices, but the binding code in your Create(CombinedStage, int[]) appears problematic given the PricelistId binding to CombinedStage. I would suggest creating a view model that reflects the form elements with an array/collection of simple values for PriceListIds in order for <select multiple>. For example, the new CombinedStageViewModel posted to the Create should have a property for `PricelistIds' such as the following for :
public IEnumerable<int> PricelistIds { get; set; }
Again, it's not entirely clear from the provided sample code or the accompanying description of the problem what the desired behavior is with respect to getting "...the entire corresponding Price value in Index Page." If you can post a sample project and define the expected results, it would make it much easier to provide a comprehensive answer to your question.
I made a simple test, you can refer to it.
I added a field to CombineStage to store your options:
public class CombineStage
{
[Key]
[ForeignKey("StageId")]
public int StageId { get; set; }
[Required(ErrorMessage = "Bạn chưa nhập tên công đoạn")]
public string Name { get; set; }
[Required(ErrorMessage = "Bạn chưa chọn công đoạn")]
[ForeignKey("PricelistId")]
public int PricelistId { get; set; }
[Required(ErrorMessage = "Bạn chưa chọn ảnh")]
public string Picture { get; set; }
[Required(ErrorMessage = "Bạn chưa nhập giá tiền")]
public int Price { get; set; }
public string PricelistIdList { get; set; }
public virtual PriceList PriceList { get; set; }
}
CombineStagesController:
public class CombineStagesController : Controller
{
private readonly ManageContext _context;
public CombineStagesController(ManageContext context)
{
_context = context;
}
// GET: Admin/CombineStages
public async Task<IActionResult> Index()
{
var manageContext = _context.CombineStage.Include(c => c.PriceList);
return View(await manageContext.ToListAsync());
}
// GET: Admin/CombineStages/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null || _context.CombineStage == null)
{
return NotFound();
}
var combineStage = await _context.CombineStage
.Include(c => c.PriceList)
.FirstOrDefaultAsync(m => m.StageId == id);
int[] PriceIdList = combineStage.PricelistIdList.Split(',').Select(n => Convert.ToInt32(n)).ToArray();
List<PriceList> priceLists = new List<PriceList>();
foreach(var PriceId in PriceIdList)
{
var PriceDetails = _context.PriceLists.Where(c => c.PricelistId == PriceId).FirstOrDefault();
priceLists.Add(PriceDetails);
}
ViewBag.priceLists = priceLists;
if (combineStage == null)
{
return NotFound();
}
return View(combineStage);
}
// GET: Admin/CombineStages/Create
public IActionResult Create()
{
var priceLists = _context.PriceLists.ToList();
List<SelectListItem> mylist = new List<SelectListItem>();
foreach (var price in priceLists)
{
mylist.Add(new SelectListItem { Text = price.Stage, Value = price.PricelistId.ToString() });
}
ViewBag.PricelistId = mylist;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Name,PricelistId,Picture,Price,PriceList")] CombineStage combineStage, int[] PricelistId)
{
int price = 0;
var stage = "";
PriceList priceList = new PriceList();
foreach (var id in PricelistId)
{
stage = _context.PriceLists.Where(m => m.PricelistId == id).FirstOrDefault().Stage.ToString();
price += _context.PriceLists.Where(m => m.PricelistId == id).FirstOrDefault().Price;
priceList = _context.PriceLists.Where(m => m.PricelistId == id).FirstOrDefault();
}
combineStage.Price = price;
combineStage.PriceList = priceList;
combineStage.PricelistIdList = string.Join(",", PricelistId);
ModelState.Clear();
if (TryValidateModel(combineStage))
{
_context.Add(combineStage);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(combineStage);
}
}
Create.cshtml:
#model _2022092602.Models.CombineStage
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>CombineStage</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PricelistId" class="control-label"></label>
<select asp-for="PricelistId" class ="form-control" asp-items="#ViewBag.PricelistId" multiple>
</select>
</div>
<div class="form-group">
<label asp-for="Picture" class="control-label"></label>
<input asp-for="Picture" class="form-control" />
<span asp-validation-for="Picture" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
Details.cshtml:
#model _2022092602.Models.CombineStage
<div>#Model.Name</div>
<div>#Model.Picture</div>
<div>#Model.Price</div>
<div>
#foreach (var price in ViewBag.priceLists)
{
<div>
<span>#price.PricelistId</span>
<span>#price.Price</span>
<span>#price.Stage</span>
</div>
}
</div>
Test Result:
Related
when i try to create a new data from the form and i do not get any error and the page just refresh , without creating any new data . im using sql server and i have a ForeignKey from another table.
my course controller:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.EntityFrameworkCore; using SchoolGrades.Models;
namespace SchoolGrades.Controllers { public class CoursesController : Controller { private readonly UniDBContext _context;
public CoursesController(UniDBContext context)
{
_context = context;
}
// GET: Courses
public async Task<IActionResult> Index()
{
var uniDBContext = _context.Courses.Include(c => c.ProfessorsAfmNavigation);
return View(await uniDBContext.ToListAsync());
}
// GET: Courses/Details/5
public async Task<IActionResult> Details(string id)
{
if (id == null || _context.Courses == null)
{
return NotFound();
}
var course = await _context.Courses
.Include(c => c.ProfessorsAfmNavigation)
.FirstOrDefaultAsync(m => m.IdCourse == id);
if (course == null)
{
return NotFound();
}
return View(course);
}
// GET: Courses/Create
public IActionResult Create()
{
ViewData["ProfessorsAfm"] = new SelectList(_context.Professors, "Afm", "Afm");
return View();
}
// POST: Courses/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("IdCourse,CourseTitle,CourseSemester,ProfessorsAfm")] Course course)
{
if (ModelState.IsValid)
{
_context.Add(course);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["ProfessorsAfm"] = new SelectList(_context.Professors, "Afm", "Afm", course.ProfessorsAfm);
return View(course);
}
// GET: Courses/Edit/5
public async Task<IActionResult> Edit(string id)
{
if (id == null || _context.Courses == null)
{
return NotFound();
}
var course = await _context.Courses.FindAsync(id);
if (course == null)
{
return NotFound();
}
ViewData["ProfessorsAfm"] = new SelectList(_context.Professors, "Afm", "Afm", course.ProfessorsAfm);
return View(course);
}
// POST: Courses/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(string id, [Bind("IdCourse,CourseTitle,CourseSemester,ProfessorsAfm")] Course course)
{
if (id != course.IdCourse)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(course);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CourseExists(course.IdCourse))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["ProfessorsAfm"] = new SelectList(_context.Professors, "Afm", "Afm", course.ProfessorsAfm);
return View(course);
}
// GET: Courses/Delete/5
public async Task<IActionResult> Delete(string id)
{
if (id == null || _context.Courses == null)
{
return NotFound();
}
var course = await _context.Courses
.Include(c => c.ProfessorsAfmNavigation)
.FirstOrDefaultAsync(m => m.IdCourse == id);
if (course == null)
{
return NotFound();
}
return View(course);
}
// POST: Courses/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(string id)
{
if (_context.Courses == null)
{
return Problem("Entity set 'UniDBContext.Courses' is null.");
}
var course = await _context.Courses.FindAsync(id);
if (course != null)
{
_context.Courses.Remove(course);
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool CourseExists(string id)
{
return (_context.Courses?.Any(e => e.IdCourse == id)).GetValueOrDefault();
}
}
}
`
my course model:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore;
namespace SchoolGrades.Models;
[Table("course")] public partial class Course { [Key] [Column("idCOURSE")] [StringLength(25)] [Unicode(false)] public string IdCourse { get; set; } = null!;
[StringLength(60)]
[Unicode(false)]
public string CourseTitle { get; set; } = null!;
[StringLength(25)]
[Unicode(false)]
public string CourseSemester { get; set; } = null!;
[Column("PROFESSORS_AFM")]
[StringLength(25)]
[Unicode(false)]
public string ProfessorsAfm { get; set; } = null!;
[InverseProperty("CourseIdCourseNavigation")]
public virtual CourseHasStudent? CourseHasStudent { get; set; }
[ForeignKey("ProfessorsAfm")]
[InverseProperty("Courses")]
public virtual Professor ProfessorsAfmNavigation { get; set; } = null!;
}
and course create view :
#model SchoolGrades.Models.Course
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Course</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="IdCourse" class="control-label"></label>
<input asp-for="IdCourse" class="form-control" />
<span asp-validation-for="IdCourse" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CourseTitle" class="control-label"></label>
<input asp-for="CourseTitle" class="form-control" />
<span asp-validation-for="CourseTitle" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CourseSemester" class="control-label"></label>
<input asp-for="CourseSemester" class="form-control" />
<span asp-validation-for="CourseSemester" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProfessorsAfm" class="control-label"></label>
<select asp-for="ProfessorsAfm" class ="form-control" asp-items="ViewBag.ProfessorsAfm"></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
i removed the if statement from the course controller .
if (ModelState.IsValid)
{ _context.Add(course);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
and i kept it like this and it worked and i could create a new data in the table after i removed the if statement .
_context.Add(course);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
any other solutions please.
I'm new to this and this might be quite easy for some people.
I'm struggling to connect a particular field (dropdown list item) between a View and a Controller. What I'm pretending is to associate a category (from a list) to a product.
For this particular Controller, my system as a Model, a Service and a ViewModel.
For the product class I have the following code:
public class DryGoodsProduct
{
public int Id { get; set; }
public string ProductName { get; set; }
[Display(Name = "Category")]
[Required]
public DryGoodsCategory DryGoodsCategory { get; set; }
public int DryGoodsCategoryId { get; set; }
public DryGoodsProduct()
{ }
public DryGoodsProduct(int id, string productName, DryGoodsCategory category)
{
Id = id;
ProductName = productName;
DryGoodsCategory = category;
}
}
The category class is the following:
public class DryGoodsCategory
{
public int Id { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public virtual ICollection<DryGoodsProduct> DryGoodsProducts { get; set; }
public DryGoodsCategory()
{
}
public DryGoodsCategory(int id, string categoryName)
{
Id = id;
CategoryName = categoryName;
}
}
The product service class is the following:
public class DryGoodsProductService
{
private readonly DB _context;
public DryGoodsProductService(DB context)
{
_context = context;
}
public async Task<List<DryGoodsProduct>> FindAllAsync()
{
return await _context.DryGoodsProducts
.Include(obj => obj.DryGoodsCategory)
.ToListAsync();
}
public async Task InsertAsync(DryGoodsProduct drygoodsProduct)
{
_context.Add(drygoodsProduct);
await _context.SaveChangesAsync();
}
public async Task<DryGoodsProduct> FindByIdAsync(int id)
{
return await _context.DryGoodsProducts
.Include(obj => obj.DryGoodsCategory)
.FirstOrDefaultAsync(x => x.Id == id);
}
public async Task RemoveAsync(int id)
{
try
{
var obj = await _context.DryGoodsProducts.FindAsync(id);
_context.DryGoodsProducts.Remove(obj);
await _context.SaveChangesAsync();
}
catch (IntegrityException e)
{
throw new IntegrityException(e.Message);
}
}
public async Task UpdateAsync(DryGoodsProduct drygoodsProduct)
{
bool hasAny = await _context.DryGoodsProducts.AnyAsync(x => x.Id == drygoodsProduct.Id);
if (!hasAny)
{
throw new NotFoundException("ID não encontrado");
}
try
{
_context.Update(drygoodsProduct);
await _context.SaveChangesAsync();
}
catch (DbConcurrencyException ex)
{
throw new DbConcurrencyException(ex.Message);
}
}
}
and the view model class is:
public class DryGoodsProductViewModel
{
public DryGoodsProduct drygoodsProduct { get; set; }
public virtual ICollection<DryGoodsCategory> DryGoodsCategories { get; set; }
}
the controller is the following:
public async Task<IActionResult> Create()
{
var categories = await _categoryService.FindAllAsync();
var product = new DryGoodsProduct();
var viewmodel = new DryGoodsProductViewModel()
{
drygoodsProduct = product,
DryGoodsCategories = categories,
};
return View(viewmodel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(DryGoodsProduct product)
{
if (ModelState.IsValid)
{
await _productService.InsertAsync(product);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(product);
}
and the create view :
enter #model Library.ViewModels.Logistics.DryGoodsProductViewModel
#{
ViewData["Title"] = "Create";
Layout = "~/Areas/Logistics/Views/Shared/_LogisticsLayout.cshtml"; }
<h1>Create</h1>
<h4>Products</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
#Html.AntiForgeryToken()
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="drygoodsProduct.ProductName" class="control-label"></label>
<input asp-for="drygoodsProduct.ProductName" class="form-control" />
<span asp-validation-for="drygoodsProduct.ProductName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="drygoodsProduct.DryGoodsCategoryId" class="control-label"></label>
<select asp-for="drygoodsProduct.DryGoodsCategoryId" asp-items="#(new SelectList(Model.DryGoodsCategories, "Id", "CategoryName"))" class="form-control"></select>
<span asp-validation-for="drygoodsProduct.DryGoodsCategoryId" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
So...
The error says:
An unhandled exception occurred while processing the request.
InvalidOperationException: The model item passed into the
ViewDataDictionary is of type
'Library.Models.Logistics.DryGoods.DryGoodsProduct', but this
ViewDataDictionary instance requires a model item of type
'Library.ViewModels.Logistics.DryGoodsProductViewModel'.
I mean, I've done something quit similar with another controller and everything is ok.
I would appreciate some feedback on how to solve this.
I'm trying to upload a image in asp .NET core. I could not upload the image.
There is no error message. all are work correctly without uploading the image.
anyone tell me what is problem in these codes
also I have a folder "Uploads" in the wwwroot folder.
I attach my codes below.
This is my Create.cshtml
#model WebApplication1.ViewModels.EmployeeViewModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Employee</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data ">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="EmployeeName" class="control-label"></label>
<input asp-for="EmployeeName" class="form-control" />
<span asp-validation-for="EmployeeName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="EmployeeProfile" class="control-label"></label>
<div class="custom-file">
<input type="file" asp-for="EmployeeProfile" class="custom-file-input" />
<label class="custom-file-label" for="customFile">Choose file</label>
</div>
<span asp-validation-for="EmployeeProfile" class="text-danger"></span>
#*<label asp-for="EmployeeImage" class="control-label"></label>
<input asp-for="EmployeeImage" class="form-control" />
<span asp-validation-for="EmployeeImage" class="text-danger"></span>*#
</div>
<div class="form-group">
<label asp-for="DepartmentId" class="control-label"></label>
<select asp-for="DepartmentId" class="form-control" asp-items="ViewBag.DepartmentId"></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-sm btn-primary rounded-0" />
<a asp-action="Index" class="btn btn-sm btn-primary rounded-0">Back to List</a>
#*<a asp-action="Index">Back to List</a>*#
</div>
</form>
</div>
</div>
#section Scripts {
<script>
// Add the following code if you want the name of the file appear on select
$(".custom-file-input").on("change", function () {
var fileName = $(this).val().split("\\").pop();
$(this).siblings(".custom-file-label").addClass("selected").html(fileName);
});
</script>
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
this is my EmployeesController
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using WebApplication1.Models;
using WebApplication1.ViewModels;
namespace WebApplication1.Controllers
{
public class EmployeesController : Controller
{
private readonly EmployeeDbContext _context;
private readonly IWebHostEnvironment webHostEnvironment;
public EmployeesController(EmployeeDbContext context, IWebHostEnvironment hostEnvironment)
{
_context = context;
webHostEnvironment = hostEnvironment;
}
// GET: Employees
//public async Task<IActionResult> Index()
//{
// var employeeDbContext = _context.Employees.Include(e => e.Department);
// return View(await employeeDbContext.ToListAsync());
//}
public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? pageNumber)
{
ViewData["CurrentSort"] = sortOrder;
ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
if (searchString != null)
{
pageNumber = 1;
}
else
{
searchString = currentFilter;
}
//var employeeDbContext = _context.Employees.Include(e => e.Department);
ViewData["CurrentFilter"] = searchString;
var employee = from em in _context.Employees.Include(e => e.Department)
select em;
// var employee = from e in _context.Employees
if (!String.IsNullOrEmpty(searchString))
{
employee = employee.Where(em => em.EmployeeName.Contains(searchString)
|| em.EmployeeName.Contains(searchString));
}
switch (sortOrder)
{
case "name_desc":
employee = employee.OrderByDescending(em => em.EmployeeName);
break;
default:
employee = employee.OrderBy(em => em.EmployeeName);
break;
}
int pageSize = 3;
return View(await PaginatedList<Employee>.CreateAsync(employee.AsNoTracking(), pageNumber ?? 1, pageSize));
//return View(await _context.Departments.ToListAsync());
}
// GET: Employees/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var employee = await _context.Employees
.Include(e => e.Department)
.FirstOrDefaultAsync(m => m.Id == id);
var EmployeeViewModel = new EmployeeViewModel()
{
Id = employee.Id,
EmployeeName = employee.EmployeeName,
DepartmentId = employee.DepartmentId,
ExistingImage = employee.EmployeeImage
};
if (employee == null)
{
return NotFound();
}
return View(employee);
}
// GET: Employees/Create
public IActionResult Create()
{
ViewData["DepartmentId"] = new SelectList(_context.Departments, "Id", "departmentName");
return View();
}
// POST: Employees/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("EmployeeName,EmployeeImage,DepartmentId")] EmployeeViewModel model)
{
if (ModelState.IsValid)
{
string uniqueFileName = ProcessUploadedFile(model);
Employee employee = new Employee
{
EmployeeName = model.EmployeeName,
DepartmentId = model.DepartmentId,
EmployeeImage = uniqueFileName
};
_context.Add(employee);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["DepartmentId"] = new SelectList(_context.Departments, "Id", "departmentName", model.DepartmentId);
return View(model);
}
// GET: Employees/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var employee = await _context.Employees.FindAsync(id);
var EmployeeViewModel = new EmployeeViewModel()
{
Id = employee.Id,
EmployeeName = employee.EmployeeName,
DepartmentId = employee.DepartmentId,
ExistingImage = employee.EmployeeImage
}; if (employee == null)
{
return NotFound();
}
ViewData["DepartmentId"] = new SelectList(_context.Departments, "Id", "departmentName", employee.DepartmentId);
return View(EmployeeViewModel);
}
// POST: Employees/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,EmployeeName,EmployeeImage,DepartmentId")] EmployeeViewModel model)
{
if (id != model.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
var employee = await _context.Employees.FindAsync(model.Id);
employee.EmployeeName = model.EmployeeName;
employee.DepartmentId = model.DepartmentId;
if (model.EmployeeProfile != null)
{
if (model.ExistingImage != null)
{
string filePath = Path.Combine(webHostEnvironment.WebRootPath, "Uploads", model.ExistingImage);
System.IO.File.Delete(filePath);
}
employee.EmployeeImage = ProcessUploadedFile(model);
}
_context.Update(employee);
await _context.SaveChangesAsync();
//try
//{
// _context.Update(model);
// await _context.SaveChangesAsync();
//}
//catch (DbUpdateConcurrencyException)
//{
// if (!EmployeeExists(model.Id))
// {
// return NotFound();
// }
// else
// {
// throw;
// }
//}
return RedirectToAction(nameof(Index));
}
ViewData["DepartmentId"] = new SelectList(_context.Departments, "Id", "departmentName", model.DepartmentId);
return View();
}
// GET: Employees/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var employee = await _context.Employees
.Include(e => e.Department)
.FirstOrDefaultAsync(m => m.Id == id);
var EmployeeViewModel = new EmployeeViewModel()
{
Id = employee.Id,
EmployeeName = employee.EmployeeName,
DepartmentId = employee.DepartmentId,
ExistingImage = employee.EmployeeImage
};
if (employee == null)
{
return NotFound();
}
return View(EmployeeViewModel);
}
// POST: Employees/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var employee = await _context.Employees.FindAsync(id);
var CurrentImage = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\Uploads", employee.EmployeeImage);
_context.Employees.Remove(employee);
if (await _context.SaveChangesAsync() > 0)
{
if (System.IO.File.Exists(CurrentImage))
{
System.IO.File.Delete(CurrentImage);
}
}
return RedirectToAction(nameof(Index));
}
private bool EmployeeExists(int id)
{
return _context.Employees.Any(e => e.Id == id);
}
private string ProcessUploadedFile(EmployeeViewModel model)
{
string uniqueFileName = null;
if (model.EmployeeProfile != null)
{
string uploadsFolder = Path.Combine(webHostEnvironment.WebRootPath, "Uploads");
// uniqueFileName = Guid.NewGuid().ToString() + "_" + model.EmployeeProfile.FileName;
uniqueFileName = Guid.NewGuid().ToString() + "_" + Path.GetFileName(model.EmployeeProfile.FileName);
string filePath = Path.Combine(uploadsFolder, uniqueFileName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
model.EmployeeProfile.CopyTo(fileStream);
}
}
return uniqueFileName;
}
}
}
This is ProcessUploadedFile method
private string ProcessUploadedFile(EmployeeViewModel model)
{
string uniqueFileName = null;
if (model.EmployeeProfile != null)
{
string uploadsFolder = Path.Combine(webHostEnvironment.WebRootPath, "Uploads");
// uniqueFileName = Guid.NewGuid().ToString() + "_" + model.EmployeeProfile.FileName;
uniqueFileName = Guid.NewGuid().ToString() + "_" + Path.GetFileName(model.EmployeeProfile.FileName);
string filePath = Path.Combine(uploadsFolder, uniqueFileName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
model.EmployeeProfile.CopyTo(fileStream);
}
}
return uniqueFileName;
}
i have 3 viewmodels, see below
EmployeeViewModel
EditImageViewModel
UploadImageViewModel
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using WebApplication1.Models;
using WebApplication1.ViewModels;
namespace WebApplication1.ViewModels
{
public class EmployeeViewModel : EditImageViewModel
{
[Required(ErrorMessage = "Please enter Employee Name")]
public string EmployeeName { get; set; }
// Navigation Properties
public int? DepartmentId { get; set; }
public Department Department { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebApplication1.ViewModels
{
public class EditImageViewModel : UploadImageViewModel
{
public int Id { get; set; }
public string ExistingImage { get; set; }
}
}
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace WebApplication1.ViewModels
{
public class UploadImageViewModel
{
[Required(ErrorMessage = "Please choose profile image")]
[Display(Name = "Profile Picture")]
public IFormFile EmployeeProfile { get; set; }
}
}
You have commented out the EmployeeImage, you only passed the EmployeeProfile to action, but
you didn't bind the EmployeeProfile.
Change to this:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("EmployeeName,EmployeeProfile,//***others")] EmployeeViewModel model)
Then I can upload the file using your code.
The [Bind] specifies which properties of a model should be included in model binding.Check:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-5.0#bind-attribute
whole code:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("EmployeeName,EmployeeProfile")] EmployeeViewModel model)
{
if (ModelState.IsValid)
{
string uniqueFileName = ProcessUploadedFile(model);
Employee employee = new Employee
{
EmployeeName = model.EmployeeName,
EmployeeImage = uniqueFileName
};
_context.Add(employee);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return RedirectToAction("Index");
}
private string ProcessUploadedFile(EmployeeViewModel model)
{
string uniqueFileName = null;
if (model.EmployeeProfile != null)
{
string uploadsFolder = Path.Combine(webHostEnvironment.WebRootPath, "Uploads");
// uniqueFileName = Guid.NewGuid().ToString() + "_" + model.EmployeeProfile.FileName;
uniqueFileName = Guid.NewGuid().ToString() + "_" + Path.GetFileName(model.EmployeeProfile.FileName);
string filePath = Path.Combine(uploadsFolder, uniqueFileName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
model.EmployeeProfile.CopyTo(fileStream);
}
}
return uniqueFileName;
}
Create.cshtml:
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="EmployeeName" class="control-label"></label>
<input asp-for="EmployeeName" class="form-control" />
<span asp-validation-for="EmployeeName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="EmployeeProfile" class="control-label"></label>
<div class="custom-file">
<input type="file" asp-for="EmployeeProfile" class="custom-file-input" />
<label class="custom-file-label" for="customFile">Choose file</label>
</div>
<span asp-validation-for="EmployeeProfile" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-sm btn-primary rounded-0" />
<a asp-action="Index" class="btn btn-sm btn-primary rounded-0">Back to List</a>
</div>
</form>
</div>
Model:
public class Employee
{
[Key]
public int Id { get; set; }
public string EmployeeName { get; set; }
public string EmployeeImage { get; set; }
}
public class EmployeeViewModel
{
public int Id { get; set; }
public string EmployeeName { get; set; }
public string LastName { get; set; }
[NotMapped]
public IFormFile EmployeeProfile { get; set; }
}
Result:
I'm trying to work out how to assign roles to users in an admin interface of my web application using ASP.Net core 2.1.
I haven't come across an answer yet. My ID for application user is string-based not an integer.
Currently I am able to edit users, add new users, add new roles delete roles, edit role names, but am unable to assign roles to users.
Ideally what I am looking to do is have a view, that has two drop-down lists. One with all users in it and a list of the available roles that I can assign.
Does anyone have any tips on how to achieve this please?
Here's my current Roles controller. For context, I am using a repository pattern. And have implemented a few of the identity models, ApplicationUser, ApplicationUserRole and Application Role.
Roles controller.cs:
[Authorize(Roles = "Admin")]
public class RolesController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager;
private IRepository _repo;
private readonly ApplicationDbContext _context;
public RolesController(UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager, IRepository repo, ApplicationDbContext context)
{
_userManager = userManager;
_roleManager = roleManager;
_repo = repo;
_context = context;
}
public IActionResult Index()
{
List<RoleListViewModel> model = new List<RoleListViewModel>();
model = _roleManager.Roles.Select(r => new RoleListViewModel
{
RoleName = r.Name,
Description = r.Description,
Id = r.Id,
NumberOfUsers = r.UserRoles.Count
}).ToList();
return View(model);
}
[AutoValidateAntiforgeryToken]
public ActionResult Details(string id)
{
var role = _repo.GetRole((string)id);
if (role == null)
{
return RedirectToAction("Index");
}
return View(role);
}
[HttpGet]
public IActionResult Create()
{
return View();
}
[AutoValidateAntiforgeryToken]
[HttpPost]
public async Task<IActionResult> Create(RoleViewModel vm)
{
if (!ModelState.IsValid)
return View(vm);
{
var role = new ApplicationRole
{ Name = vm.Name };
var result = await _roleManager.CreateAsync(role);
if (result.Succeeded)
{
_repo.AddRole(role);
return RedirectToAction("Index");
}
else
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
return View(vm);
}
}
[HttpGet]
public ActionResult Delete(string Id)
{
var role = _context.Roles.Find(Id);
if (role == null)
{
return RedirectToAction("Index");
}
return View(role);
}
[ValidateAntiForgeryToken]
[HttpPost]
public async Task<ActionResult> Delete([Bind(include: "Id,Name")]ApplicationRole myRole)
{
ApplicationRole role = _context.Roles.Find(myRole.Id);
_context.Roles.Remove(role);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult Edit(string Id)
{
var role = _repo.GetRole((string)Id);
if (role == null)
{
return RedirectToAction("Index");
}
return View(new RoleViewModel { Id = role.Id, Name = role.Name, Description = role.Description });
}
[HttpPost]
public async Task<IActionResult> Edit(RoleViewModel vm)
{
var role = await _roleManager.FindByIdAsync(vm.Id);
if (vm.Name != role.Name)
{
role.Name = vm.Name;
}
if(vm.Description != role.Description)
{
role.Description = vm.Description;
}
var result = _roleManager.UpdateAsync(role).Result;
if (result.Succeeded)
{
return RedirectToAction("Index", "Roles");
}
else return View(vm);
}
//[HttpGet]
//public async Task<IActionResult> AssignRole(string Id)
//{
// List<UserRolesViewModel> model = new List<UserRolesViewModel>();
// model = _userManager.Users.Select(r => new UserRolesViewModel
// {
// Email = u.Email,
// Description = r.Description,
// Id = r.Id,
// NumberOfUsers = r.UserRoles.Count
// }).ToList();
// return View(model);
//}`
ApplicationUser.cs:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; internal set; }
public string LastName { get; internal set; }
public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
public virtual ICollection<IdentityUserToken<string>> Tokens { get; set; }
public virtual IEnumerable<ApplicationRole> Roles { get; set; }
public ICollection<ApplicationUserRole> UserRoles { get; set; }
public ICollection<MainComment> MainComments { get; set; }
}
ApplicationUserRole.cs
public class ApplicationUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
ApplicationRole.cs
public class ApplicationRole : IdentityRole
{
public ApplicationRole() : base() { }
public ApplicationRole(string name)
: base(name)
{ }
public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
public string Description { get; set; }
}
If you want to assign a role to a user in MVC (tested in asp.net core 2.1), you can do the following. I have also created a user here, just to show the injection of the UserManager.
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace MyApp.Controllers
{
public class RolesController : Controller
{
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<IdentityUser> _userManager;
public RolesController(RoleManager<IdentityRole> roleManager, UserManager<IdentityUser> userManager)
{
_roleManager = roleManager;
_userManager = userManager;
}
[HttpPost]
public async Task<IActionResult> AssignRoleToUser(string _roleName, string _userName)
{
//Created a user
var user = new IdentityUser { UserName = _userName, Email = "xyz#somedomain.tld" };
var result = await _userManager.CreateAsync(user, "[SomePassword]");
if (result.Succeeded)
{
// assign an existing role to the newly created user
await _userManager.AddToRoleAsync(user, "Admin");
}
return View();
}
}
}
By no means this is the proper way of doing this or anything.
I made a role assigner not so long ago and this is what i came up with.
Also the "Admin" role cannot be assigned. It can be simply commented out/removed. and the class:
HomebreModel
only contains strings
The Assign view shows two dropdowns one for the user and another one for the role.
The Controller
[Authorize(AuthenticationSchemes = HomebrewModel.BothAuthSchemes, Roles = HomebrewModel.RoleAdmin)]
public class RoleController : Controller
{
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<NutricionUser> _userManager;
public RoleController(RoleManager<IdentityRole> roleManager, UserManager<NutricionUser> userManager)
{
_roleManager = roleManager;
_userManager = userManager;
}
// GET: Role
public async Task<ActionResult> Index()
{
var adminRole = await _roleManager.FindByNameAsync(HomebrewModel.RoleAdmin);
var assignableRoles = _roleManager.Roles.ToList();
assignableRoles.RemoveAt(assignableRoles.IndexOf(adminRole));
return View(assignableRoles);
}
// GET: Role/Assign
public async Task<ActionResult> Assign()
{
var adminRole = await _roleManager.FindByNameAsync(HomebrewModel.RoleAdmin);
var assignableRoles = _roleManager.Roles.ToList();
assignableRoles.RemoveAt(assignableRoles.IndexOf(adminRole));
ViewData["Name"] = new SelectList(assignableRoles, "Name", "Name");
ViewData["UserName"] = new SelectList(_userManager.Users, "UserName", "UserName");
return View(new RoleModel());
}
// POST: Role/Assign
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Assign(RoleModel roleModel)
{
if (ModelState.IsValid)
{
if(roleModel.Name == HomebrewModel.RoleAdmin)
{
ViewData["Message"] = "Invalid Request.";
return View("Info");
}
var user = await _userManager.FindByEmailAsync(roleModel.UserName);
if (user != null)
{
if (await _roleManager.RoleExistsAsync(roleModel.Name))
{
if(await _userManager.IsInRoleAsync(user, roleModel.Name))
{
ViewData["Message"] = $#"User {roleModel.UserName} already has the {roleModel.Name} role.";
return View("Info");
}
else
{
await _userManager.AddToRoleAsync(user, roleModel.Name);
ViewData["Message"] = $#"User {roleModel.UserName} was assigned the {roleModel.Name} role.";
return View("Info");
}
}
else
{
ViewData["Message"] = "Invalid Request.";
return View("Info");
}
}
else
{
ViewData["Message"] = "Invalid Request.";
return View("Info");
}
}
return View(roleModel);
}
}
And these are the views.
Index
#{
ViewData["Title"] = "Roles";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Roles</h2>
<p>
<a asp-action="Assign">Assign</a>
</p>
<table class="table">
<thead>
<tr>
<th>
Roles
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#item.Name
</td>
</tr>
}
</tbody>
</table>
Assign
#model Models.RoleModel
#{
ViewData["Title"] = "Assign";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Assign</h2>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Assign">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<select asp-for="Name" asp-items="(SelectList)#ViewData["Name"]"></select>
</div>
<div class="form-group">
<label asp-for="UserName" class="control-label"></label>
<select asp-for="UserName" asp-items="(SelectList)#ViewData["UserName"]"></select>
</div>
<div class="form-group">
<input type="submit" value="Assign" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to list.</a>
</div>
And this is the RoleModel class
public class RoleModel
{
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "UserName")]
public string UserName { get; set; }
}
The Info view as well
#{
ViewData["Title"] = "Info";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Info</h2>
<h3>#ViewData["Message"]</h3>
I am trying to make a Web App which has Parent-Child model (multiple model) in one view (ASP.NET Core + MVC + Entity Framework).
The following is input (input No.1).
This is "Views/Blogs/index.cshtml"
The following is also input (input No.2).
This is "Views/Tags/index.cshtml"
The following is my expecting output (output No.1).
I have written the following "Views/Blogs/cshtml".
#model IEnumerable<urlapp12.Models.UrlTag>
#{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Blog.Userid)
</th>
<th>
#Html.DisplayNameFor(model => model.Blog.Url)
</th>
<th>
#Html.DisplayNameFor(model => model.Blog.LastUpdatedAt_UtcDt)
</th>
<th>
#Html.DisplayNameFor(model => model.Blog.LastUpdatedAt_LocalDt)
</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Blog.Userid)
</td>
<td>
#Html.DisplayFor(modelItem => item.Blog.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Blog.LastUpdatedAt_UtcDt)
</td>
<td>
#Html.DisplayFor(modelItem => item.Blog.LastUpdatedAt_LocalDt)
</td>
<td>
#foreach (var childItem in item.Tag)
{
#Html.DisplayFor(modelItem => childItem.tagItem)
}
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.Blog.BlogId">Edit</a> |
<a asp-action="Details" asp-route-id="#item.Blog.BlogId">Details</a> |
<a asp-action="Delete" asp-route-id="#item.Blog.BlogId">Delete</a>
</td>
</tr>
}
</tbody>
</table>
And executed, I have gotten the following error.
(output No.2. The real unexpected output)
An unhandled exception occurred while processing the request.
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List`1[urlapp12.Models.Blog]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IEnumerable`1[urlapp12.Models.UrlTag]'.
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary.EnsureCompatible(object value)
Models/Blog.cs is the following.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace urlapp12.Models
{
public partial class Blog
{
public Blog()
{
Post = new HashSet<Post>();
}
[Key]
public int BlogId { get; set; }
public string Userid { get; set; }
public string Url { get; set; }
public string Title { get; set; }
public string CreatedBy { get; set; }
[System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
public DateTime CreatedAt_UtcDt { get; set; }
[System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
public DateTime CreatedAt_LocalDt { get; set; }
public string LastUpdatedBy { get; set; }
[System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
public DateTime LastUpdatedAt_UtcDt { get; set; }
[System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
public DateTime LastUpdatedAt_LocalDt { get; set; }
public ICollection<Tag> Tag { get; set; }
public ICollection<Post> Post { get; set; }
/*
public List<Tag> Tag { get; set; }
public List<Post> Post { get; set; }
*/
}
}
Models/Tag.cs is the following.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace urlapp12.Models
{
public class Tag
{
public int Id { get; set; }
public int DispOrderNbr { get; set; }
public string tagItem { get; set; }
public int BlogId { get; set; }
public string CreatedBy { get; set; }
[System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
public DateTime CreatedAt_UtcDt { get; set; }
[System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
public DateTime CreatedAt_LocalDt { get; set; }
public string LastUpdatedBy { get; set; }
[System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
public DateTime LastUpdatedAt_UtcDt { get; set; }
[System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
public DateTime LastUpdatedAt_LocalDt { get; set; }
[System.ComponentModel.DataAnnotations.Schema.ForeignKey("BlogId")]
public Blog blog { get; set; }
}
}
Model/UlrTag.cs is the following.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace urlapp12.Models
{
public class UrlTag
{
public Blog Blog { get; set; }
public IEnumerable<Tag> Tag { get; set; }
}
}
Does someone help about this Parent-Child model?
Thank you in advance.
Blogs.Controller.cs is the following.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using urlapp12.Models;
namespace urlapp12.Controllers
{
public class BlogsController : Controller
{
private readonly Blogging02Context _context;
// Stores UserManager
private readonly UserManager<ApplicationUser> _manager;
private UserManager<ApplicationUser> _userManager;
public BlogsController(Blogging02Context context, UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
_context = context;
}
// GET: Blogs
public async Task<IActionResult> Index()
{
return View(await _context.Blog.ToListAsync());
}
// GET: Blogs/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var blog = await _context.Blog
.SingleOrDefaultAsync(m => m.BlogId == id);
if (blog == null)
{
return NotFound();
}
return View(blog);
}
// GET: Blogs/Create
public IActionResult Create()
{
return View();
}
// POST: Blogs/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost] [ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("BlogId,Userid,Url,Title")] Blog blog)
{
if (ModelState.IsValid)
{
/*
string strCurrentUserId;
strCurrentUserId = User.Identity.GetUserId(this IIdentity identity);
var currentUserName = User.Identity.Name ;
var user = await UserManager<ApplicationUser>.FindByIdAsync(User.Identity.GetUserId());
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new MyDbContext()));
var UserManager = new UserManager(IUserstore<ApplicationUser>);
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
var user = await GetCurrentUserAsync();
var userId = user?.Id;
string mail = user?.Email;
var userid = GetCurrentUserClaims().userid;
var userClaims = new UserClaims();
var claims = _httpContextAccessor.HttpContext.User.Claims.ToList();
var userid2 = await IGenericRepository < User > userRepository.GetByIdAsync(_currentUserGuid);
UserManager<ApplicationUser> _userManager;
SignInManager<ApplicationUser> _signInManager = new SignInManager<ApplicationUser>();
var info = await _signInManager.GetExternalLoginInfoAsync();
*/
// Stores UserManager
// private readonly UserManager<ApplicationUser> _manager;
// var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
// var result = await _userManager.CreateAsync(user, model.Password);
var user = await _userManager.GetUserAsync(HttpContext.User);
var currentLoginUserid = user.Id;
blog.Userid = user.Id;
int maxIdInDb = 0;
int BlogRecCnt = _context.Blog.Count();
if (_context.Blog.Count() == 0)
{
maxIdInDb = 0;
}
else
{
maxIdInDb = _context.Blog.Max(p => p.BlogId);
}
int NextId = maxIdInDb + 1;
blog.BlogId = NextId;
blog.CreatedAt_LocalDt = DateTime.Now;
blog.CreatedAt_UtcDt = DateTime.UtcNow;
blog.CreatedBy = user.Id;
blog.LastUpdatedAt_LocalDt = DateTime.Now;
blog.LastUpdatedAt_UtcDt = DateTime.UtcNow;
blog.LastUpdatedBy = user.Id;
_context.Add(blog);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(blog);
}
// GET: Blogs/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var blog = await _context.Blog.SingleOrDefaultAsync(m => m.BlogId == id);
if (blog == null)
{
return NotFound();
}
var user = await _userManager.GetUserAsync(HttpContext.User);
var currentLoginUserid = user.Id;
blog.Userid = user.Id;
blog.LastUpdatedAt_LocalDt = DateTime.Now;
blog.LastUpdatedAt_UtcDt = DateTime.UtcNow;
blog.LastUpdatedBy = user.Id;
return View(blog);
}
// POST: Blogs/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("BlogId,Userid,Url,Title")] Blog blog)
{
if (id != blog.BlogId)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(blog);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!BlogExists(blog.BlogId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(blog);
}
// GET: Blogs/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var blog = await _context.Blog
.SingleOrDefaultAsync(m => m.BlogId == id);
if (blog == null)
{
return NotFound();
}
return View(blog);
}
// POST: Blogs/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var blog = await _context.Blog.SingleOrDefaultAsync(m => m.BlogId == id);
_context.Blog.Remove(blog);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool BlogExists(int id)
{
return _context.Blog.Any(e => e.BlogId == id);
}
}
}
TagsController.cs is the following.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using urlapp12.Models;
namespace urlapp12.Controllers
{
public class TagsController : Controller
{
private readonly Blogging02Context _context;
public TagsController(Blogging02Context context)
{
_context = context;
}
//int id, [Bind("BlogId,Userid,Url,Title")] Blog blog
// GET: Tags
// public async Task<IActionResult> Index()
public async Task<IActionResult> Index(int id, [Bind("BlogId,Userid,Url,Title")] Blog blog)
{
/*
return View(await _context.Tag.ToListAsync());
*/
var blogging02Context = _context.Tag.Include(t => t.blog);
return View(await blogging02Context.ToListAsync());
// return View (await _context.Tag.ToListAsync());
}
// GET: Tags/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var tag = await _context.Tag
.Include(t => t.blog)
.SingleOrDefaultAsync(m => m.Id == id);
if (tag == null)
{
return NotFound();
}
return View(tag);
}
// GET: Tags/Create
public IActionResult Create()
{
ViewData["BlogId"] = new SelectList(_context.Blog, "BlogId", "Title");
return View();
}
// POST: Tags/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,DispOrderNbr,tagItem,BlogId")] Tag tag)
{
if (ModelState.IsValid)
{
_context.Add(tag);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["BlogId"] = new SelectList(_context.Blog, "BlogId", "Title", tag.BlogId);
return View(tag);
}
// GET: Tags/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var tag = await _context.Tag.SingleOrDefaultAsync(m => m.Id == id);
if (tag == null)
{
return NotFound();
}
ViewData["BlogId"] = new SelectList(_context.Blog, "BlogId", "Title", tag.BlogId);
return View(tag);
}
// POST: Tags/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,DispOrderNbr,tagItem,BlogId")] Tag tag)
{
if (id != tag.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(tag);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TagExists(tag.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["BlogId"] = new SelectList(_context.Blog, "BlogId", "Title", tag.BlogId);
return View(tag);
}
// GET: Tags/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var tag = await _context.Tag
.Include(t => t.blog)
.SingleOrDefaultAsync(m => m.Id == id);
if (tag == null)
{
return NotFound();
}
return View(tag);
}
// POST: Tags/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int? id)
{
var tag = await _context.Tag.SingleOrDefaultAsync(m => m.Id == id);
_context.Tag.Remove(tag);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool TagExists(int id)
{
return _context.Tag.Any(e => e.Id == id);
}
}
}
junkangli and Gimly, thank you for reply.
I have tried the Gimly's idea.
public async Task<IActionResult> Index()
{
return View(await _context.Blog.Include(b => b.Tags).ToListAsync());
}
Then Visual Studio told me that "'Blog' does not include 'Tags' definition" error.
So I have changed Tags to Tag, My Visual Studio says it is O.K.
public async Task<IActionResult> Index()
{
return View(await _context.Blog.Include(b => b.Tag).ToListAsync());
}
I run my codes for Debug mode, the web application returned the following error.
An unhandled exception occurred while processing the request.
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List1[urlapp12.Models.Blog]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IEnumerable1[urlapp12.Models.UrlTag]'.
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary.EnsureCompatible(object value)
Thank you very much.
I have changed the code "/Views/Blogs/Index.cshml" like the following, and I could execute it successfully.
#model IEnumerable<urlapp12.Models.Blog>
#{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Userid)
</th>
<th>
#Html.DisplayNameFor(model => model.Url)
</th>
<th>
#Html.DisplayNameFor(model => model.LastUpdatedAt_UtcDt)
</th>
<th>
#Html.DisplayNameFor(model => model.LastUpdatedAt_LocalDt)
</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Userid)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastUpdatedAt_UtcDt)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastUpdatedAt_LocalDt)
</td>
<td>
#foreach (var childItem in item.Tag)
{
#Html.DisplayFor(itemItem => childItem.tagItem)
}
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.BlogId">Edit</a> |
<a asp-action="Details" asp-route-id="#item.BlogId">Details</a> |
<a asp-action="Delete" asp-route-id="#item.BlogId">Delete</a>
</td>
</tr>
}
</tbody>
</table>
First, about the exception you're getting, the error message is pretty explicit, and #junkangli explained it will in its comment, you're not returning the correct object to the View. View expects an IEnumerable<UrlTag> and you're sending it an IEnumerable<Blog>.
Now, about the core of your issue, you'll want to load the list of tags in your query for getting the list of Blogs, so, in your controller's Index action, you should do something like:
public async Task<IActionResult> Index()
{
return View(await _context.Blog.Include(b => b.Tags).ToListAsync());
}
Then, in your view, you should be able to access your tags and create a while loop to display all your tags.