I'm looking to create a real estate website. I'm using ASP.NET Core 6 MVC to build my website. I create a Property table and an ImageData table. I used imagesharp for image processing. And I used one to many relationship to store images. I connected those previous mentioned table by using a foreign key. But the problem is, it's not going to store my images. But other information it's storing inside the database table. Only images are not storing in the database.
Can anyone help me to solve this problem. I'm waiting for your answers.
Below is my code.
Property.cs
using CeylonPropertiesVIP.Data;
using System.ComponentModel.DataAnnotations;
namespace CeylonPropertiesVIP.Models
{
public class Property
{
[Key]
public int Id { get; set; }
[Required]
public string? Title { get; set; }
[Required]
public string? PropertyType { get; set; }
[Required]
public string? Address { get; set; }
[Required]
public string? Bedrooms { get; set; }
[Required]
public string? Bathrooms { get; set; }
[Required]
public string? FloorArea { get; set; }
[Required]
public string? NoOfFloors { get; set; }
[Required]
public string? CarParkingSpaces { get; set; }
[Required]
public string? FurnishingStatus { get; set; }
[Required]
public string? AreaOfLands { get; set; }
[Required]
public string? Availability { get; set; }
[Required]
public string? NearestBusStop { get; set; }
[Required]
public string? AgeOfTheProperty { get; set; }
[Required]
public string? PropertyDescription { get; set; }
[Required]
public string? PropertyFeatures { get; set; }
public string? PropertyVideo { get; set; }
public string? GoogleMapLink { get; set; }
[Required]
public string? Name { get; set; }
[Required]
public string? ContactNumber { get; set; }
public string? Email { get; set; }
public List<ImageData>? ImageDatas { get; set; }
}
}
ImageData.cs
using CeylonPropertiesVIP.Models;
namespace CeylonPropertiesVIP.Data
{
public class ImageData
{
// int Id -> use MD5(MD5(Id)) to proptect against bots.
public ImageData() => this.Id = Guid.NewGuid();
public Guid Id { get; set; }
public string OriginalFileName { get; set; }
public string OriginalType { get; set; }
public byte[] OriginalContent { get; set; }
public byte[] ThumbnailContent { get; set; }
public byte[] FullscreenContent { get; set; }
public Property Property { get; set; }
}
}
Create.cshtml
#model CeylonPropertiesVIP.Models.Property
#{
ViewData["Title"] = "Create";
}
<section class="py-5">
<div class="container px-5 my-5">
<h1>Create</h1>
<h4>Property</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="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PropertyType" class="control-label"></label>
<input asp-for="PropertyType" class="form-control" />
<span asp-validation-for="PropertyType" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Address" class="control-label"></label>
<input asp-for="Address" class="form-control" />
<span asp-validation-for="Address" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Bedrooms" class="control-label"></label>
<input asp-for="Bedrooms" class="form-control" />
<span asp-validation-for="Bedrooms" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Bathrooms" class="control-label"></label>
<input asp-for="Bathrooms" class="form-control" />
<span asp-validation-for="Bathrooms" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FloorArea" class="control-label"></label>
<input asp-for="FloorArea" class="form-control" />
<span asp-validation-for="FloorArea" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="NoOfFloors" class="control-label"></label>
<input asp-for="NoOfFloors" class="form-control" />
<span asp-validation-for="NoOfFloors" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CarParkingSpaces" class="control-label"></label>
<input asp-for="CarParkingSpaces" class="form-control" />
<span asp-validation-for="CarParkingSpaces" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FurnishingStatus" class="control-label"></label>
<input asp-for="FurnishingStatus" class="form-control" />
<span asp-validation-for="FurnishingStatus" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AreaOfLands" class="control-label"></label>
<input asp-for="AreaOfLands" class="form-control" />
<span asp-validation-for="AreaOfLands" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Availability" class="control-label"></label>
<input asp-for="Availability" class="form-control" />
<span asp-validation-for="Availability" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="NearestBusStop" class="control-label"></label>
<input asp-for="NearestBusStop" class="form-control" />
<span asp-validation-for="NearestBusStop" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AgeOfTheProperty" class="control-label"></label>
<input asp-for="AgeOfTheProperty" class="form-control" />
<span asp-validation-for="AgeOfTheProperty" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PropertyDescription" class="control-label"></label>
<input asp-for="PropertyDescription" class="form-control" />
<span asp-validation-for="PropertyDescription" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PropertyFeatures" class="control-label"></label>
<input asp-for="PropertyFeatures" class="form-control" />
<span asp-validation-for="PropertyFeatures" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PropertyVideo" class="control-label"></label>
<input asp-for="PropertyVideo" class="form-control" />
<span asp-validation-for="PropertyVideo" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="GoogleMapLink" class="control-label"></label>
<input asp-for="GoogleMapLink" class="form-control" />
<span asp-validation-for="GoogleMapLink" class="text-danger"></span>
</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="ContactNumber" class="control-label"></label>
<input asp-for="ContactNumber" class="form-control" />
<span asp-validation-for="ContactNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ImageDatas" class="control-label">Property Images</label>
<input asp-for="ImageDatas" type="file" multiple="multiple" name="images" class="form-control" />
<span asp-validation-for="ImageDatas" class="text-danger"></span>
</div>
<div class="form-group my-2">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
#*</div>
</div>*#
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
</section>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
PropertiesCotroller.cs
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 CeylonPropertiesVIP.Data;
using CeylonPropertiesVIP.Models;
namespace CeylonPropertiesVIP.Controllers
{
public class PropertiesController : Controller
{
private readonly ApplicationDbContext _context;
public PropertiesController(ApplicationDbContext context)
{
_context = context;
}
// GET: Properties
public async Task<IActionResult> Index()
{
return _context.Property != null ?
View(await _context.Property.ToListAsync()) :
Problem("Entity set 'ApplicationDbContext.Property' is null.");
}
// GET: Properties/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null || _context.Property == null)
{
return NotFound();
}
var #property = await _context.Property
.FirstOrDefaultAsync(m => m.Id == id);
if (#property == null)
{
return NotFound();
}
return View(#property);
}
// GET: Properties/Create
public IActionResult Create()
{
return View();
}
// POST: Properties/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("Id,Title,PropertyType,Address,Bedrooms,Bathrooms,FloorArea,NoOfFloors,CarParkingSpaces,FurnishingStatus,AreaOfLands,Availability,NearestBusStop,AgeOfTheProperty,PropertyDescription,PropertyFeatures,PropertyVideo,GoogleMapLink,Name,ContactNumber,Email")] Property #property)
{
if (ModelState.IsValid)
{
_context.Add(#property);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(#property);
}
// GET: Properties/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null || _context.Property == null)
{
return NotFound();
}
var #property = await _context.Property.FindAsync(id);
if (#property == null)
{
return NotFound();
}
return View(#property);
}
// POST: Properties/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,Title,PropertyType,Address,Bedrooms,Bathrooms,FloorArea,NoOfFloors,CarParkingSpaces,FurnishingStatus,AreaOfLands,Availability,NearestBusStop,AgeOfTheProperty,PropertyDescription,PropertyFeatures,PropertyVideo,GoogleMapLink,Name,ContactNumber,Email")] Property #property)
{
if (id != #property.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(#property);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PropertyExists(#property.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(#property);
}
// GET: Properties/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null || _context.Property == null)
{
return NotFound();
}
var #property = await _context.Property
.FirstOrDefaultAsync(m => m.Id == id);
if (#property == null)
{
return NotFound();
}
return View(#property);
}
// POST: Properties/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
if (_context.Property == null)
{
return Problem("Entity set 'ApplicationDbContext.Property' is null.");
}
var #property = await _context.Property.FindAsync(id);
if (#property != null)
{
_context.Property.Remove(#property);
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool PropertyExists(int id)
{
return (_context.Property?.Any(e => e.Id == id)).GetValueOrDefault();
}
}
}
You can try to remove the name in <input asp-for="ImageDatas" .../>,use List<IFormFile> to get the data from form,and add ImageDatas into [Bind]:
Property:
public class Property
{
[Key]
public int Id { get; set; }
[Required]
public string? Title { get; set; }
[Required]
public string? PropertyType { get; set; }
[Required]
public string? Address { get; set; }
[Required]
public string? Bedrooms { get; set; }
[Required]
public string? Bathrooms { get; set; }
[Required]
public string? FloorArea { get; set; }
[Required]
public string? NoOfFloors { get; set; }
[Required]
public string? CarParkingSpaces { get; set; }
[Required]
public string? FurnishingStatus { get; set; }
[Required]
public string? AreaOfLands { get; set; }
[Required]
public string? Availability { get; set; }
[Required]
public string? NearestBusStop { get; set; }
[Required]
public string? AgeOfTheProperty { get; set; }
[Required]
public string? PropertyDescription { get; set; }
[Required]
public string? PropertyFeatures { get; set; }
public string? PropertyVideo { get; set; }
public string? GoogleMapLink { get; set; }
[Required]
public string? Name { get; set; }
[Required]
public string? ContactNumber { get; set; }
public string? Email { get; set; }
public List<IFormFile> ImageDatas { get; set; }=new List<IFormFile>();
}
Create.cshtml:
<section class="py-5">
<div class="container px-5 my-5">
<h1>Create</h1>
<h4>Property</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="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PropertyType" class="control-label"></label>
<input asp-for="PropertyType" class="form-control" />
<span asp-validation-for="PropertyType" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Address" class="control-label"></label>
<input asp-for="Address" class="form-control" />
<span asp-validation-for="Address" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Bedrooms" class="control-label"></label>
<input asp-for="Bedrooms" class="form-control" />
<span asp-validation-for="Bedrooms" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Bathrooms" class="control-label"></label>
<input asp-for="Bathrooms" class="form-control" />
<span asp-validation-for="Bathrooms" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FloorArea" class="control-label"></label>
<input asp-for="FloorArea" class="form-control" />
<span asp-validation-for="FloorArea" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="NoOfFloors" class="control-label"></label>
<input asp-for="NoOfFloors" class="form-control" />
<span asp-validation-for="NoOfFloors" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CarParkingSpaces" class="control-label"></label>
<input asp-for="CarParkingSpaces" class="form-control" />
<span asp-validation-for="CarParkingSpaces" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FurnishingStatus" class="control-label"></label>
<input asp-for="FurnishingStatus" class="form-control" />
<span asp-validation-for="FurnishingStatus" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AreaOfLands" class="control-label"></label>
<input asp-for="AreaOfLands" class="form-control" />
<span asp-validation-for="AreaOfLands" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Availability" class="control-label"></label>
<input asp-for="Availability" class="form-control" />
<span asp-validation-for="Availability" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="NearestBusStop" class="control-label"></label>
<input asp-for="NearestBusStop" class="form-control" />
<span asp-validation-for="NearestBusStop" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AgeOfTheProperty" class="control-label"></label>
<input asp-for="AgeOfTheProperty" class="form-control" />
<span asp-validation-for="AgeOfTheProperty" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PropertyDescription" class="control-label"></label>
<input asp-for="PropertyDescription" class="form-control" />
<span asp-validation-for="PropertyDescription" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PropertyFeatures" class="control-label"></label>
<input asp-for="PropertyFeatures" class="form-control" />
<span asp-validation-for="PropertyFeatures" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PropertyVideo" class="control-label"></label>
<input asp-for="PropertyVideo" class="form-control" />
<span asp-validation-for="PropertyVideo" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="GoogleMapLink" class="control-label"></label>
<input asp-for="GoogleMapLink" class="form-control" />
<span asp-validation-for="GoogleMapLink" class="text-danger"></span>
</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="ContactNumber" class="control-label"></label>
<input asp-for="ContactNumber" class="form-control" />
<span asp-validation-for="ContactNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ImageDatas" class="control-label">Property Images</label>
<input asp-for="ImageDatas" type="file" multiple="multiple" class="form-control" />
<span asp-validation-for="ImageDatas" class="text-danger"></span>
</div>
<div class="form-group my-2">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
#*</div>
</div>*#
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
</section>
action:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,PropertyType,Address,Bedrooms,Bathrooms,FloorArea,NoOfFloors,CarParkingSpaces,FurnishingStatus,AreaOfLands,Availability,NearestBusStop,AgeOfTheProperty,PropertyDescription,PropertyFeatures,PropertyVideo,GoogleMapLink,Name,ContactNumber,Email,ImageDatas")] Property #property)
{
if (id != #property.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(#property);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PropertyExists(#property.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(#property);
}
Then you can try to convert List<IFormFile> to List<ImageData> in Create action.Here is a sample
List<ImageData> filedatas = new List<ImageData>();
if (property.ImageDatas.Count() > 0) {
foreach (var item in #property.ImageDatas)
{
using (var ms = new MemoryStream())
{
item.CopyTo(ms);
var fileBytes = ms.ToArray();
filedatas.Add(new ImageData { OriginalFileName=item.FileName, OriginalContent=fileBytes });
}
}
//add filedatas to database
}
I'm not understanding how to properly bind a single item from a collection. In my project, a student can have multiple plans with these models:
public class Student
{
public Student()
{
Plans = new HashSet<Plan>();
}
public int StudentId { get; set; }
public string Designee { get; set; }
public string DesigneePhone { get; set; }
public virtual ICollection<Plan> Plans { get; set; }
}
public partial class Plan
{
public int PlanId { get; set; }
public int StudentId { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public virtual Student Student { get; set; }
}
I'm listing each plan with its own Save button on the page. I thought I was using the asp-for helper correctly according to this Learn Razor Pages post.
#page "{studentId:int}"
#model CollectionTest.PlansModel
<form asp-page-handler="SaveMain" method="post">
<div class="row mt-3">
<input type="hidden" asp-for="Student.StudentId" />
<div class="col">
<label asp-for="Student.Designee"></label>
<input asp-for="Student.Designee" class="form-control" />
</div>
<div class="col">
<label asp-for="Student.DesigneePhone"></label>
<input asp-for="Student.DesigneePhone" class="form-control" />
</div>
<div class="col-auto align-self-end">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
</form>
<hr />
#for (int i = 0; i < Model.Student.Plans.Count(); i++)
{
var planId = Model.Student.Plans.ToList()[i].PlanId.ToString();
<div id="#("card_Plan" + planId)" class="card">
<div class="card-header">
<h5>Plan ##planId</h5>
</div>
<div class="card-body">
<form id="#("form_Plan" + planId)" asp-page-handler="SavePlan" method="post">
<input type="hidden" asp-for="Student.Plans.ToList()[i].PlanId" />
<div class="row">
<div class="col">
<label asp-for="Student.Plans.ToList()[i].StartDate"></label>
<input asp-for="Student.Plans.ToList()[i].StartDate" class="form-control" />
<span asp-validation-for="Student.Plans.ToList()[i].StartDate"></span>
</div>
<div class="col">
<label asp-for="Student.Plans.ToList()[i].EndDate"></label>
<input asp-for="Student.Plans.ToList()[i].EndDate" class="form-control" />
<span asp-validation-for="Student.Plans.ToList()[i].EndDate"></span>
</div>
</div>
</form>
</div>
<div class="card-footer">
<div class="float-right">
<button type="submit" class="btn btn-primary" form="#("form_Plan" + planId)">Save</button>
</div>
</div>
</div>
}
But it seems like nothing is being bound in the SavePlan page handler:
[BindProperty]
public Student Student { get; set; }
.
.
.
public async Task<IActionResult> OnPostSavePlan(int planId)
{
if (!ModelState.IsValid)
{
return Page();
}
/******************************
*
* Student.Plans is empty and planId is zero
*
*******************************/
Plan plan = await _planContext.Plan.FirstAsync(p => p.PlanId == planId);
_planContext.Attach(plan).State = EntityState.Modified;
try
{
await _planContext.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PlanExists(plan.PlanId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Plans");
}
What am I doing wrong?
According to your code, because what you pass in the razor page is [0].PlanId, but what you receive in the OnPostSavePlan method is planId. If you only need to receive planId, you need to change the form.
<div class="card-body">
<form id="#("form_Plan" + planId)" asp-page-handler="SavePlan" method="post">
<input type="hidden" asp-for="#planId" />
From your requirement,it seems you want to pass a list of plans. So you need to modify your code in the following steps:
Plans.cshtml:
<form id="form_Plan" asp-page-handler="SavePlan" method="post">
#for (int i = 0; i < Model.Student.Plans.Count(); i++)
{
var planId = Model.Student.Plans.ToList()[i].PlanId.ToString();
<div id="#("card_Plan" + planId)" class="card">
<div class="card-header">
<h5>Plan ##planId</h5>
</div>
<div class="card-body">
<input type="hidden" asp-for="Student.Plans.ToList()[i].PlanId" />
<div class="row">
<div class="col">
<label asp-for="Student.Plans.ToList()[i].StartDate"></label>
<input asp-for="Student.Plans.ToList()[i].StartDate" class="form-control" />
<span asp-validation-for="Student.Plans.ToList()[i].StartDate"></span>
</div>
<div class="col">
<label asp-for="Student.Plans.ToList()[i].EndDate"></label>
<input asp-for="Student.Plans.ToList()[i].EndDate" class="form-control" />
<span asp-validation-for="Student.Plans.ToList()[i].EndDate"></span>
</div>
</div>
</div>
</div>
}
<div class="card-footer">
<div class="float-right">
<button type="submit" class="btn btn-primary" form="form_Plan">Save</button>
</div>
</div>
</form>
Plans.cshtml.cs:
public class PlansModel : PageModel
{
public IActionResult OnGet()
{
//for easy testing,I add the data manually
Student = new Student()
{
Plans = new List<Plan>()
{
new Plan(){ PlanId=1, StartDate=DateTime.Parse("2019-8-7")},
new Plan(){ PlanId=2, StartDate=DateTime.Parse("2019-4-7")},
new Plan(){ PlanId=3, StartDate=DateTime.Parse("2019-6-7")}
}
};
return Page();
}
[BindProperty]
public Student Student { get; set; }
public async Task<IActionResult> OnPostSavePlan(List<Plan> plans)
{
//...
}
}
Result:
Update:
Plans.cshtml:
#foreach (var plan in Model.Student.Plans)
{
var planId = plan.PlanId.ToString();
<div id="#("card_Plan" + planId)" class="card">
<div class="card-header">
<h5>Plan ##planId</h5>
</div>
<div class="card-body">
<form id="#("form_Plan" + planId)" asp-page-handler="SavePlan" method="post">
<input type="hidden" asp-for="#plan.PlanId" />
<div class="row">
<div class="col">
<label asp-for="#plan.StartDate"></label>
<input asp-for="#plan.StartDate" class="form-control" />
<span asp-validation-for="#plan.StartDate"></span>
</div>
<div class="col">
<label asp-for="#plan.EndDate"></label>
<input asp-for="#plan.EndDate" class="form-control" />
<span asp-validation-for="#plan.EndDate"></span>
</div>
</div>
</form>
</div>
<div class="card-footer">
<div class="float-right">
<button type="submit" class="btn btn-primary" form="#("form_Plan" + planId)">Save</button>
</div>
</div>
</div>
}
Plans.cshtml.cs:
public async Task<IActionResult> OnPostSavePlan(Plan plan)
{
//...
}
Result:
I have a ViewModel named CreateRoleViewModel which contains the items shown below. On the view, I'm iterating through a list of permissions and displaying them. What I'm trying to accomplish is to allow a user to create a new role and add permissions to that role. I've been able to successfully pass all data to the controller except the permissions. Hopefully I'm not confusing anyone. I've tried to use a for loop (which did't produce correct results) and a foreach loop, which did produce the correct results (meaning displayed all permissions currently available). I realize I'm going to also, most likely need to update my domain model for Permissions to include columns for View, Modify etc.
CreateViewModel
public class CreateRoleViewModel
{
[Required]
public string RoleName { get; set; }
public string RoleDescription { get; set; }
public List<Permissions> Permissions { get; set; }
public bool AllowViewAccess { get; set; }
public bool AllowModifyAccess { get; set; }
public CreateRoleViewModel()
{
Permissions = new List<Permissions>();
}
}
PermissionsController
[Route("Administration/Permissions/CreateRole")]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateRole(CreateRoleViewModel newRole)
{
try
{
var test = newRole;
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
View
<form method="post" asp-action="CreateRole">
<div class="form-group col-6" style="padding-left:0">
<label asp-for="RoleName"></label>
<input asp-for="RoleName" class="form-control" />
<span asp-validation-for="RoleName" class="text-danger"></span>
</div>
<div class="form-group col-6" style="padding-left:0">
<label asp-for="RoleDescription"></label>
<input asp-for="RoleDescription" class="form-control" />
</div>
<div class="form-group ">
<h4>Permissions</h4>
<div class="card">
<div class="card-header">
<input type="checkbox" class="form-check-input p-2" id="selectAll">
<label class="form-check-label" for="selectAll">Select All<span style="font-weight:bold;font-style:italic;"> (This will allow read/write access to ALL selected items.)</span></label>
</div>
</div>
<!--Permissions should go here-->
<div class="card-body border">
#foreach (var item in Model.Permissions)
{
<div class="row" style="border-radius:3px;border-top:2px solid gray;border-bottom:2px solid gray;padding:15px;">
<div class="col-8">
<input type="checkbox" class="form-check-input p-2" id="select">
<label class="form-check-label" for="select" style="font-weight:bold;">#item.PermissionName</label>
<p style="color:gray;">#item.PermissionDescription</p>
</div>
<div class="col-2">
<input type="checkbox" class="form-check-input p-2" id="readOnly" asp-for="AllowViewAccess">
<label class="form-check-label" for="readOnly">View</label>
</div>
<div class="col-2">
<input type="checkbox" class="form-check-input p-2" id="readWrite" asp-for="AllowModifyAccess">
<label class="form-check-label" for="readWrite">Modify</label>
</div>
</div>
}
</div>
<div asp-validation-summary="All" class="text-info"></div>
<button style="background-color: #6987D5;color:white;" class="btn">Save</button>
<button class="btn" style="background-color:lightgray;border:1px solid darkgray">Cancel</button>
</div>
</form>
From your description and the view code, I think you should put the "AllowViewAccess " and "AllowModifyAccess " in the Permissions class, because the role have separate "View" or "Modify" access to different permissions. Based on your codes, I made an example:
Model:
public class CreateRoleViewModel
{
[Required]
public string RoleName { get; set; }
public string RoleDescription { get; set; }
public List<Permissions> Permissions { get; set; }
public CreateRoleViewModel()
{
Permissions = new List<Permissions>();
}
}
public class Permissions
{
public string PermissionName { get; set; }
public string PermissionDescription { get; set; }
public bool AllowViewAccess { get; set; }
public bool AllowModifyAccess { get; set; }
}
View:
#model CreateRoleViewModel
#{
var i = 0;
}
<form method="post" asp-action="CreateRole">
<div class="form-group col-6" style="padding-left:0">
<label asp-for="RoleName"></label>
<input asp-for="RoleName" class="form-control" />
<span asp-validation-for="RoleName" class="text-danger"></span>
</div>
<div class="form-group col-6" style="padding-left:0">
<label asp-for="RoleDescription"></label>
<input asp-for="RoleDescription" class="form-control" />
</div>
<div class="form-group ">
<h4>Permissions</h4>
<div class="card">
<div class="card-header">
<input type="checkbox" class="form-check-input p-2" id="selectAll">
<label class="form-check-label" for="selectAll">Select All<span style="font-weight:bold;font-style:italic;"> (This will allow read/write access to ALL selected items.)</span></label>
</div>
</div>
<!--Permissions should go here-->
<div class="card-body border">
#foreach (var item in Model.Permissions)
{
<div class="row" style="border-radius:3px;border-top:2px solid gray;border-bottom:2px solid gray;padding:15px;">
<div class="col-8">
<input type="checkbox" class="form-check-input p-2" id="select">
<input type="hidden" asp-for="#Model.Permissions[i].PermissionName">
<label class="form-check-label" for="select" style="font-weight:bold;">#item.PermissionName</label>
<p style="color:gray;">#item.PermissionDescription</p>
<input type="hidden" asp-for="#Model.Permissions[i].PermissionDescription">
</div>
<div class="col-2">
<input type="checkbox" class="form-check-input p-2" id="readOnly" asp-for="#Model.Permissions[i].AllowViewAccess">
<label class="form-check-label" for="readOnly">View</label>
</div>
<div class="col-2">
<input type="checkbox" class="form-check-input p-2" id="readWrite" asp-for="#Model.Permissions[i].AllowModifyAccess">
<label class="form-check-label" for="readWrite">Modify</label>
</div>
</div>
i++;
}
</div>
<div asp-validation-summary="All" class="text-info"></div>
<button style="background-color: #6987D5;color:white;" class="btn">Save</button>
<button class="btn" style="background-color:lightgray;border:1px solid darkgray">Cancel</button>
</div>
</form>
Controller:
public IActionResult Index()
{
CreateRoleViewModel createRoleViewModel = new CreateRoleViewModel();
return View(createRoleViewModel);
}
//[Route("Administration/Permissions/CreateRole")]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateRole(CreateRoleViewModel newRole)
{
try
{
var test = newRole;
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
Result:
I'm trying to update a table(FlightClasses) with multiple rows having relation of one to many with table(Flight) but i can't update it with the changed values
Can anyone help me with that
Model class of Flight
i want to update rows on other table of flightclasses linked with row in this table
namespace Airline.Models
{
public class FlightModel
{
public int Id { get; set; }
public string FlightName { get; set; }
public string FlightNo { get; set; }
public string OriginCity { get; set; }
public string DestinationCity { get; set; }
public DateTime Departure { get; set; }
public DateTime Arrival { get; set; }
public virtual ICollection<FlightClassesModel> FlightClasses { get; set; }
}
}
Model class of FlightClasses
namespace Airline.Models
{
public class FlightClassesModel
{
public int FlightClassesModelId { get; set; }
public type Class { get; set; }
public int AvailableSeats { get; set; }
public double Price { get; set; }
public virtual FlightModel Flight { get; set; }
public int? FlightId { get; set; }
}
public enum type
{
Business_Class,
First_Class,
Club_Class
}
}
View
#model Airline.Models.FlightModel
#{
ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<h4>FlightModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="FlightName" class="control-label"></label>
<input asp-for="FlightName" class="form-control" />
<span asp-validation-for="FlightName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FlightNo" class="control-label"></label>
<input asp-for="FlightNo" class="form-control" />
<span asp-validation-for="FlightNo" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="OriginCity" class="control-label"></label>
<input asp-for="OriginCity" class="form-control" />
<span asp-validation-for="OriginCity" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DestinationCity" class="control-label"></label>
<input asp-for="DestinationCity" class="form-control" />
<span asp-validation-for="DestinationCity" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Departure" class="control-label"></label>
<input asp-for="Departure" class="form-control" />
<span asp-validation-for="Departure" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Arrival" class="control-label"></label>
<input asp-for="Arrival" class="form-control" />
<span asp-validation-for="Arrival" class="text-danger"></span>
</div>
#foreach (var flightClass in Model.FlightClasses)
{
<div class="form-group">
<label asp-for="#flightClass.Class" class="control-label"></label>
<select class="form-control" asp-for="#flightClass.Class" asp-items="Html.GetEnumSelectList<type>()"></select>
<span asp-validation-for="#flightClass.Class" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#flightClass.AvailableSeats" class="control-label"></label>
<input asp-for="#flightClass.AvailableSeats" class="form-control" />
<span asp-validation-for="#flightClass.AvailableSeats" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#flightClass.Price" class="control-label"></label>
<input asp-for="#flightClass.Price" class="form-control" />
<span asp-validation-for="#flightClass.Price" class="text-danger"></span>
</div>
}
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
controller
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var flightModel = await _context.Flight.FindAsync(id);
if (flightModel == null)
{
return NotFound();
}
return View(flightModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, FlightModel flightModel)
{
if (id != flightModel.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(flightModel);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!FlightModelExists(flightModel.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(flightModel);
}
This code _context.Update(flightModel); will update flightModel only, so if you wanna update FlightClasses also you must get FlightClasses from the flightModel then use _context.Update(flightClasse); for all the instances in FlightClasses. And at the end you use await _context.SaveChangesAsync();
Updating data in the Parent-Child(one to many relation) table using EntityFramework Core
we first retrieve the FlightModel entity from the database using FirstOrDefaultAsync
var entity = await _context.FlightModel
.Include(s => s.FlightClassesModel)
.FirstOrDefaultAsync(s => s.FlightModelId == id);
use SetValues on the CurrentValues of the Entry method to update the properties of the FlightModel
_context.Entry(entity).CurrentValues.SetValues(FlightModel);
Loop through the FlightClassesModel collection to update or add new child entities as needed.
foreach (var child in FlightModel.FlightClassesModel)
{
var childEntity = entity.FlightClassesModel.FirstOrDefault(c => c.FlightClassesModelId == child.FlightClassesModelId);
if (childEntity != null)
{
_context.Entry(childEntity).CurrentValues.SetValues(child);
}
else
{
entity.FlightClassesModel.Add(child);
}
}
SaveChangesAsync to persist the changes to the database.
await _context.SaveChangesAsync();
I'm trying to add a "Create" to one of my Controllers (LeagueController), so I created a Create View, which uses my League object.
However, when I go to submit the form, it does not redirect back to the Index view, as I want it to, nor does it enter the log entry to say that I created a league.
League Class
public class League : BaseEntity
{
[Required]
[DataType(DataType.Text)]
public string LeagueName { get; set; }
[Required]
[DataType(DataType.Text)]
public string LeagueInitials { get; set; }
[DataType(DataType.Text)]
public string LeagueURL { get; set; }
[DataType(DataType.DateTime)]
public DateTime Founded { get; set; }
[InverseProperty("League")]
public ICollection<Team> Teams { get; set; }
[ForeignKey("LeagueID")]
public ICollection<LeagueOwners> LeagueOwners { get; set; }
}
LeaguesController Class
public class LeaguesController : Controller
{
private MyDBContext context;
private ILogger logger;
public LeaguesController(MyDBContext context, ILogger logger)
{
this.context = context;
this.logger = logger;
}
public IActionResult Index()
{
this.logger.LogInformation("Reached League Index");
return View();
}
[Route("Create")]
public IActionResult Create()
{
this.logger.LogInformation("Creating a league");
return View();
}
[HttpPost]
public IActionResult Create(League league)
{
this.logger.LogInformation("Create button clicked!");
return this.RedirectToAction("Index");
}
}
Create.cshtml
#model MySite.Core.Entities.League
#{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
<h4>League</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="LeagueName" class="control-label"></label>
<input asp-for="LeagueName" class="form-control" />
<span asp-validation-for="LeagueName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LeagueInitials" class="control-label"></label>
<input asp-for="LeagueInitials" class="form-control" />
<span asp-validation-for="LeagueInitials" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LeagueURL" class="control-label"></label>
<input asp-for="LeagueURL" class="form-control" />
<span asp-validation-for="LeagueURL" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Founded" class="control-label"></label>
<input asp-for="Founded" class="form-control" />
<span asp-validation-for="Founded" class="text-danger"></span>
</div>
<div class="form-group" hidden="hidden">
<label asp-for="Created" class="control-label"></label>
<input asp-for="Created" class="form-control" value="#DateTime.Now" />
<span asp-validation-for="Created" class="text-danger"></span>
</div>
<div class="form-group" hidden="hidden">
<label asp-for="Modified" class="control-label"></label>
<input asp-for="Modified" class="form-control" value="#DateTime.Now" />
<span asp-validation-for="Modified" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
The first way is that you could delete [Route("Create")]which is on your get method.
The second way is that you could add [Route] attribute to your post method like below:
[Route("Create")]
public IActionResult Create()
{
this.logger.LogInformation("Creating a league");
return View();
}
[Route("Create")]
[HttpPost]
public IActionResult Create(League league)
{
this.logger.LogInformation("Create button clicked!");
return this.RedirectToAction("Index");
}
In your form <form asp-action="Create" method="post"> add a tag to point to the correct controller asp-controller="Leagues"
Do not enforce the route, that's just anti-pattern.