Image is not added / read in image upload field (asp.net) - c#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using eCom.Data;
using eCom.Models;
using IHostingEnvironment = Microsoft.AspNetCore.Hosting.IHostingEnvironment;
namespace eCom.Areas.Admin.Controllers
{
[Area("Admin")]
public class ProductController : Controller
{
private ApplicationDbContext _db;
private IHostingEnvironment _he;
public ProductController(ApplicationDbContext db, IHostingEnvironment he)
{
_db = db;
_he = he;
}
public IActionResult Index()
{
return View(_db.Products.Include(c => c.ProductTypes).Include(f => f.SpecialTag).ToList());
}
//POST Index action method
[HttpPost]
public IActionResult Index(decimal? lowAmount, decimal? largeAmount)
{
var products = _db.Products.Include(c => c.ProductTypes).Include(c => c.SpecialTag)
.Where(c => c.Price >= lowAmount && c.Price <= largeAmount).ToList();
if (lowAmount == null || largeAmount == null)
{
products = _db.Products.Include(c => c.ProductTypes).Include(c => c.SpecialTag).ToList();
}
return View(products);
}
//Get Create method
public IActionResult Create()
{
ViewData["productTypeId"] = new SelectList(_db.ProductTypes.ToList(), "Id", "ProductType");
ViewData["TagId"] = new SelectList(_db.SpecialTag.ToList(), "Id", "Tag");
return View();
}
//Post Create method
[HttpPost]
public async Task<IActionResult> Create(Products products, IFormFile image)
{
if (ModelState.IsValid)
{
/*var searchProduct = _db.Products.FirstOrDefault(c => c.Tag == products.Tag);
if (searchProduct != null)
{
ViewBag.message = "This product is already exist";
ViewData["productTypeId"] = new SelectList(_db.ProductTypes.ToList(), "Id", "ProductType");
ViewData["TagId"] = new SelectList(_db.SpecialTag.ToList(), "Id", "Tag");
return View(products);
}*/
if (image != null)
{
var name = Path.Combine(_he.WebRootPath + "/Images", Path.GetFileName(image.FileName));
await image.CopyToAsync(new FileStream(name, FileMode.Create));
products.Image = "Images/" + image.FileName;
}
/*if (image == null)
{
products.Image = "Images/noimage.PNG";
}*/
_db.Products.Add(products);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(products);
}
I'm trying to make a website with asp.net. I'm a beginner. the theme of this website is the development of an online store with various products. everything went well until I had to create the first product (which, of course, also needs an image)
I don't get any errors from the compiler, I don't understand what the problem is. below is the specific piece of code for creating a new product.
after completing all the fields and adding an image click "save" and I receive this:
https://imgur.com/qwpnwNJ
below is also the part of the Create View for Products
<h2 class="text-info">Add New Product</h2>
<form asp-action="Create" method="post" enctype="multipart/form-data">
<div class="p-4 rounded border">
<div asp-validation-summary="ModelOnly" class="text-danger">
</div>
<h3>#ViewBag.message</h3>
<div class="form-group row">
<div class="col-2">
<label asp-for="Tag">Nume</label>
</div>
<div class="col-5">
<input asp-for="Tag" class="form-control" />
</div>
<span asp-validation-for="Tag" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Price"></label>
</div>
<div class="col-5">
<input asp-for="Price" class="form-control" />
</div>
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="GetImage("></label>
</div>
<div class="col-5">
<input asp-for="GetImage()" class="form-control" type="file" />
</div>
<span asp-validation-for="GetImage()" class="text-danger"></span>
</div>

Related

How to delete two entities in one click ASP.NET CORE MVC

I have in button delete which delete Ticket Entities and I add another entities call Discussion.
When user click on Details page it is load Discussion.
Now before I implement Discussion DeleteTicket method works fine, but now I add children and Ticket couldn't be deleted anymore.
My question is : Can I delete two entities at one time, or I need to delete first children entities and then parent ?
I try to google and try to find some soultion but unfortunettly anything which I find is not usefull for me.
DEMO
Here is my code :
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
using System.Linq;
using System.Security.Claims;
using VmSTicketing.DataAccess.Data;
using VmSTicketing.DataAccess.Repository.IRepository;
using VmSTicketing.Models;
using VmSTicketing.Models.Enum;
using VmSTicketing.Models.ViewModels;
using VmSTicketing.Utility;
namespace VmSTicketing.Areas.Manager.Controllers
{
[Area("Manager")]
[Authorize(Roles = SD.Role_Admin + "," + SD.Role_Manager)]
public class TicketController : Controller
{
private readonly IUnitOfWork _unitOfwork;
private readonly ApplicationDbContext _db;
public TicketController(IUnitOfWork unitOfwork, ApplicationDbContext db)
{
_unitOfwork = unitOfwork;
_db = db;
}
public IActionResult Index()
{
return View();
}
public IActionResult Upsert(int? Id)
{
TicketVM ticketVM = new TicketVM()
{
Ticket = new Ticket(),
TicketTypeList = _unitOfwork.TicketType.GetAll().Select(i => new SelectListItem
{
Text = i.Name,
Value = i.Id.ToString()
}),
ApplicationUser = new ApplicationUser(),
Client = new Client()
};
if (Id == null)
{
return View(ticketVM);
}
ticketVM.Ticket = _unitOfwork.Ticket.Get(Id.GetValueOrDefault());
if (ticketVM.Ticket == null)
{
NotFound();
}
return View(ticketVM);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Upsert(TicketVM ticketVM)
{
var users = _unitOfwork.ApplicationUser.GetAll(x => x.Id == x.UserName);
var userName = User.FindFirstValue(ClaimTypes.Email);
var user = HttpContext.User.Identity.Name;
if (ModelState.IsValid)
{
if (ticketVM.Ticket.Id == 0)
{
ticketVM.Ticket.ApplicationUser = _db.ApplicationUsers.FirstOrDefault(u => u.Email == userName);
ticketVM.Ticket.Status = TicketStatus.Otvoren.ToString();
_unitOfwork.Ticket.Add(ticketVM.Ticket);
}
//else
//{
// ticketVM.Ticket.ApplicationUser = _db.ApplicationUsers.FirstOrDefault(u => u.Email == userName);
// ticketVM.Ticket.Status = ((TicketStatus)Convert.ToInt32(ticketVM.Ticket.Status)).ToString();
// _unitOfwork.Ticket.Update(ticketVM.Ticket);
//}
_unitOfwork.Save();
return RedirectToAction(nameof(Index));
}
return View(ticketVM);
}
public IActionResult TicketStatusEdit(int ticketId)
{
var ticket = _unitOfwork.Ticket.Get(ticketId);
ticket.Status = ((TicketStatus)Convert.ToInt32(ticket.Status)).ToString();
_unitOfwork.Save();
return View();
}
public IActionResult Details(int? Id)
{
TicketVM ticketVM = new TicketVM()
{
Ticket = _unitOfwork.Ticket.Get(Id.GetValueOrDefault()),
};
if (Id == null)
{
return NotFound();
}
var ticketType = _unitOfwork.Ticket.GetAll(includeProperties: "TicketType,ApplicationUser");
ViewBag.discussion = _unitOfwork.Discussion.GetAll(d => d.TicketId == Id).OrderBy(d => d.Id).ToList();
return ticketVM.Ticket != null ? View(ticketVM) : NotFound();
}
[HttpPost]
public IActionResult SendDiscussion(int ticketId,string message)
{
var userName = User.FindFirstValue(ClaimTypes.Email);
var account = _db.ApplicationUsers.SingleOrDefault(a => a.UserName.Equals(userName));
var discussion = new Discussion();
discussion.DateAndTime = DateTime.Now;
discussion.Content = message;
discussion.TicketId = ticketId;
discussion.UserId = account.Id;
_unitOfwork.Discussion.Add(discussion);
_unitOfwork.Save();
return RedirectToAction("Details", new { id = ticketId});
}
#region API CALLS
[HttpGet]
public IActionResult GetByName(string name)
{
var obj = _unitOfwork.ApplicationUser.GetAll(x => x.Name == name);
//var obj = _unitOfwork.Ticket.GetAll(x => x.ApplicationUser.Name == name);
return Json(new { data = obj });
}
[HttpGet]
public IActionResult GetAll()
{
var username = User.FindFirst(ClaimTypes.Name).Value;
var account = _unitOfwork.ApplicationUser.GetFirstOrDefault(a => a.UserName.Equals(username));
var isAdministrator = User.IsInRole("Admin");
var obj = _unitOfwork.Ticket.GetAll(filter: x => (isAdministrator || x.UserId == account.FirstOrDefault().Id), includeProperties: "TicketType,ApplicationUser");
return Json(new { data = obj });
}
[HttpDelete]
[Authorize(Roles = SD.Role_Admin)]
public IActionResult Delete(int id)
{
var objFromDb = _unitOfwork.Ticket.Get(id);
if (objFromDb == null)
{
return Json(new { success = false, message = "Error while deleting" });
}
_unitOfwork.Ticket.Remove(objFromDb);
_unitOfwork.Save();
return Json(new { success = true, message = "Delete Successfull" });
}
#endregion
}
}
Details.cs
#model VmSTicketing.Models.ViewModels.TicketVM
#using VmSTicketing.Utility
#{
ViewData["Title"] = "Details";
Layout = "~/Views/Shared/AdminLTE/_Layout.cshtml";
}
<h2 class="text-info">Pregled tiketa</h2>
<div class="p-4 border rounded">
<div class="form-group row">
<div class="col-2">
<label>Vrsta tiketa</label>
</div>
<div class="col-5">
<input asp-for="Ticket.TicketType.Name" disabled class="form-control" />
</div>
</div>
<div class="form-group row">
<div class="col-2">
<label>Opis</label>
</div>
<div class="col-5">
<textarea asp-for="Ticket.Description" disabled class="form-control"></textarea>
</div>
</div>
#if (User.IsInRole(SD.Role_Admin))
{
<input type="hidden" asp-for="Ticket.Id" />
<div class="form-group row">
<div class="col-2">
<label>User</label>
</div>
<div class="col-5">
<input asp-for="Ticket.ApplicationUser.Name" disabled class="form-control" />
</div>
</div>
<div class="form-group row">
<div class="col-2">
<label>Datum i vrijeme slanja</label>
</div>
<div class="col-5">
<input asp-for="Ticket.DateAndTime" disabled class="form-control" />
</div>
</div>
<div class="form-group row">
<div class="col-2">
<label>Status Tiketa</label>
</div>
<div class="col-5">
<select asp-for="#Model.Ticket.Status" asp-items="Html.GetEnumSelectList<VmSTicketing.Models.Enum.TicketStatus>()" class="form-control"></select>
</div>
</div>
}
<br />
<div class="form-group">
<a asp-action="Index" class="btn btn-success">Back to List</a>
</div>
</div>
<hr />
<div class="row">
<div class="col-md-12">
<div class="card card-primary direct-chat direct-chat-primary">
<div class="card-header">
<h3 class="card-title">Discussion</h3>
</div>
<div class="card-body">
<div class="direct-chat-messages">
#if (ViewBag.discussion != null)
{
#foreach (var discussion in ViewBag.discussion)
{
<div class="direct-chat-msg #(discussion.ApplicationUser.Role == "Admin" ? "": "right")">
<div class="direct-chat-infos clearfix">
<span class="direct-chat-name #(discussion.ApplicationUser.Role == "Admin" ? "float-right": "float-left")">#discussion.ApplicationUser.Name</span>
<span class="direct-chat-timestamp #(discussion.ApplicationUser.Role == "Admin" ? "float-left": "float-right")">#discussion.DateAndTime.ToString("dd/MM/yyyy HH:mm")</span>
</div>
<div class="direct-chat-text">
#discussion.Content
</div>
</div>
}
}
</div>
</div>
<div class="card-footer">
<form asp-controller="Ticket" asp-action="SendDiscussion" method="post">
<div class="input-group">
<input type="text" name="message" placeholder="Type Message ..." class="form-control">
<span class="input-group-append">
<button type="submit" class="btn btn-primary">Send</button>
</span>
</div>
<input type="hidden" name="ticketId" asp-for="Ticket.Id" />
</form>
</div>
</div>
</div>
</div>
Sorry for not adding enought source code I don't want to make this post only code, but If you need more source please let me know I will add later on.
if post is not so clean for you, please check DEMO and I hope it will be clear.
Cheers !
UPDATE
I add funtion to remove Discussion but It show me error message 405
[HttpDelete]
public IActionResult DeleteDiscussion(int id)
{
var discussion = new Discussion();
_unitOfwork.Discussion.Remove(discussion);
_unitOfwork.Save();
return View(discussion);
}
<a asp-area="Manager" asp-controller="Ticket" asp-action="DeleteDiscussion" class="btn btn-danger">Delete Discussion</a>
As #JamieD77 mentioned you need to delete child first, then delete the parent. Assuming your child entity is Discussion and parent is Ticket, you can do something like this. (if you don't have cascade delete implemented.)
[HttpDelete]
[Authorize(Roles = SD.Role_Admin)]
public IActionResult Delete(int id)
{
var objFromDb = _unitOfwork.Ticket.Get(id);
if (objFromDb == null)
{
return Json(new { success = false, message = "Record not found" });
}
if (objFromDb.Discussion != null) //if you have Discussion associated with Ticket delete that first.
{
try
{
_unitOfwork.Discussion.Remove(objFromDb.Discussion);
_unitOfwork.Save();
}
catch(Exception e)
{
return Json(new { success = false, message = "Error while deleting" });
}
}
try
{
_unitOfwork.Ticket.Remove(objFromDb);
_unitOfwork.Save();
}
catch(Exception e)
{
return Json(new { success = false, message = "Error while deleting" });
}
return Json(new { success = true, message = "Delete Successfull" });
}

Image won't upload to REST api

I'm having trouble to update the image of an author in my application. When I add the input field for files in my Razor pages the code doesn't jump to the OnPost method in the code behind. Without the file input field it does.
Below my code.
Razor page code:
#page
#model PersonalLibrary.Razor.Pages.Authors.EditModel
#{
}
<div class="container">
<h1>Edit</h1>
<h4>Author</h4>
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Author.Id" />
<div class="form-group">
<img src="#Html.DisplayFor(model => model.Author.Image)" />
<div class="file-field input-field">
<div class="btn">
<span>Browse</span>
<input asp-for="Author.Image" type="file" />
</div>
<div class="file-path-wrapper">
<input asp-for="Author.Image" class="file-path validate" type="text" placeholder="Upload file" />
</div>
</div>
</div>
<div class="form-group">
<label asp-for="Author.Name" class="control-label"></label>
<input asp-for="Author.Name" class="form-control" />
<span asp-validation-for="Author.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Author.Country" class="control-label"></label>
<input asp-for="Author.Country" class="form-control" />
<span asp-validation-for="Author.Country" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Author.DateOfBirth" class="control-label"></label>
<input asp-for="Author.DateOfBirth" class="datepicker form-control" />
<span asp-validation-for="Author.DateOfBirth" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Update" class="btn btn-primary" />
</div>
</form>
</div>
OnPost in the code behind:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
IFormFile image = (IFormFile)Author.Image;
var imageUri = $"{baseUri}/authors/{Author.Id}/images";
await WebApiClient.PutCallApi<Author, IFormFile>(imageUri, image);
var uri = $"{baseUri}/authors/{Author.Id}";
await WebApiClient.PutCallApi<Author, Author>(uri, Author);
return RedirectToPage("./Index");
}
Api controller methods:
[HttpPut("{id}")]
public async Task<IActionResult> PutAsync(AuthorRequestDto authorRequestDto)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var authorResponseDto = await _authorService.UpdateAsync(authorRequestDto);
return Ok(authorResponseDto);
}
[HttpPost("{id}/image"), HttpPut("{id}/image")]
public async Task<IActionResult> Image([FromRoute] Guid id, IFormFile image)
{
var author = await _authorService.AddOrUpdateImageAsync(id, image);
if (author == null)
{
return NotFound($"Author with ID {id} not found");
}
return Ok(author);
}
Any help welcome!
UPDATE
In the Edit.cshtml.cs file I've added a new BindProperty for the image.
namespace PersonalLibrary.Razor.Pages.Authors
{
public class EditModel : PageModel
{
private readonly string baseUri = Constants.ApiBaseUri.BaseUri;
[BindProperty]
public Author Author { get; set; }
[BindProperty]
public IFormFile Image { get; set; }
public async Task<IActionResult> OnGetAsync(Guid id)
{
if (id == null)
{
return NotFound();
}
var uri = $"{baseUri}/authors/{id}";
Author = await WebApiClient.GetApiResult<Author>(uri);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var imageUri = $"{baseUri}/authors/{Author.Id}/image";
await WebApiClient.PutCallApi<IFormFile, IFormFile>(imageUri, Image);
//var uri = $"{baseUri}/authors/{Author.Id}";
//await WebApiClient.PutCallApi<Author, Author>(uri, Author);
return RedirectToPage("./Index");
}
}
}
I had to change the enctype for the form to "multipart/form-data" in the Edit.cshtml file, like shown below.
#page
#model PersonalLibrary.Razor.Pages.Authors.EditModel
#{
}
<div class="container">
<h1>Edit</h1>
<h4>Author</h4>
<form method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Author.Id" />
<div class="form-group">
<img src="#Html.DisplayFor(model => model.Author.Image)" />
<div class="file-field input-field">
<div class="btn">
<span>Browse</span>
<input asp-for="Image" type="file" />
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text"
placeholder="Upload file" />
</div>
</div>
</div>
<div class="form-group">
<input type="submit" value="Update" class="btn btn-primary" />
</div>
</form>
</div>
So now the Image property is filled with the image I want to upload, but it crashes when accessing the WebApiClient class.
namespace PersonalLibrary.Web.Helpers
{
public class WebApiClient
{
private static JsonMediaTypeFormatter GetJsonFormatter() {
var formatter = new JsonMediaTypeFormatter();
//prevent self-referencing loops when saving Json (Bucket -> BucketItem -> Bucket -> ...)
formatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
return formatter;
}
public async static Task<T> GetApiResult<T>(string uri)
{
using (HttpClient httpClient = new HttpClient())
{
string response = await httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObject<T>(response, GetJsonFormatter().SerializerSettings);
}
}
public static async Task<TOut> PutCallApi<TOut, TIn>(string uri, TIn entity)
{
return await CallApi<TOut, TIn>(uri, entity, HttpMethod.Put);
}
public static async Task<TOut> PostCallApi<TOut, TIn>(string uri, TIn entity)
{
return await CallApi<TOut, TIn>(uri, entity, HttpMethod.Post);
}
public static async Task<TOut> DeleteCallApi<TOut>(string uri)
{
return await CallApi<TOut, object>(uri, null, HttpMethod.Delete);
}
private static async Task<TOut> CallApi<TOut, TIn>(string uri, TIn entity, HttpMethod httpMethod)
{
TOut result = default;
using (HttpClient httpClient = new HttpClient())
{
HttpResponseMessage response;
if (httpMethod == HttpMethod.Post)
{
response = await httpClient.PostAsync(uri, entity, GetJsonFormatter());
}
else if (httpMethod == HttpMethod.Put)
{
response = await httpClient.PutAsync(uri, entity, GetJsonFormatter());
}
else
{
response = await httpClient.DeleteAsync(uri);
}
result = await response.Content.ReadAsAsync<TOut>();
}
return result;
}
}
}
This is the error I'm receiving:
UnsupportedMediaTypeException: No MediaTypeFormatter is available to read an object of type 'IFormFile' from content with media type 'application/problem+json'.
System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
My guess is the problem is I use a JsonMediaTypeFormatter. But I can't figure out what to use to handle files or images.

ViewBag is not sending any value to the view

In ASP.NET Core MVC, I'm facing trouble creating a login panel, I'm using sessions after the user is logged into the account and I'm storing the session values inside the ViewBag. But the ViewBag does not get any value inside of it, it rather gets null value inside it.
Here's the controller
[HttpPost]
public IActionResult Login(userModel model)
{
var findValue = _context.users.Any(o => o.username == model.username);
var findValue2 = _context.users.Any(o => o.password == model.password);
if (findValue && findValue2)
{
HttpContext.Session.SetString("Username", model.username);
}
return View(model);
}
public IActionResult Index()
{
ViewBag.Username = HttpContext.Session.GetString("Username");
return View();
}
And here's the view
Index.cshtml
#model ComplaintManagement.Models.userModel
#{
ViewData["Title"] = "Portal";
}
<h1>Welcome #ViewBag.Username</h1>
Login.cshtml
#model ComplaintManagement.Models.userModel
#{
ViewData["Title"] = "Login";
}
<div class="row mb-3">
<div class="col-lg-4"></div>
<div class="col-lg-4 border login" style="background-color: #d3d1d1;">
<h4 class="mt-3 text-center">
<i class="fa fa-lg fa-user text-secondary"></i><br />
Login
</h4>
<hr />
<form method="post" asp-action="Index" asp-controller="Portal">
<div class="text-danger"></div>
<div class="text-warning">#ViewBag.Name</div>
<div class="form-group">
<label class="mt-4 asp-for=" username"">Username</label>
<input class="form-control" type="text" required="required" asp-for="username" />
<span></span>
</div>
<div class="form-group">
<label class="mt-4" asp-for="password">Password</label>
<input type="password" class="form-control" required="required" asp-for="password" />
<span></span>
</div>
<center>Don't have an account? <a asp-controller="Portal" asp-action="Register">Register here</a>.</center>
<center><button value="login" class="btn btn-primary mt-3 w-25 mb-3 align-content-center">Login</button></center>
</form>
</div>
<div class="col-lg-4"></div>
</div>
Session and state management in ASP.NET Core
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1
Here is a demo How to use Session in ASP.NET Core.
1. Codes of Startup Configurations
AddSession in ConfigureServices, UseSession in Configure.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSession();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSession();
app.UseStaticFiles();
....
}
2. Codes of Controller
public class AccountController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpGet]
public IActionResult Login()
{
return View();
}
[HttpPost]
public IActionResult Login(userModel model)
{
if (string.IsNullOrEmpty(model.username
) || string.IsNullOrEmpty(model.password))
{
return NotFound();
}
//var user = await _context.users.FirstOrDefaultAsync(x => x.username == model.username && x.password == model.password);
//if (user != null)
if (model.username.Equals("test") && model.password.Equals("123"))
{
HttpContext.Session.SetString("username", model.username);
}
else
ViewBag.error = "Invalid Account";
return View("Index");
}
[HttpGet]
public IActionResult Logout()
{
HttpContext.Session.Remove("username");
return RedirectToAction("Index");
}
}
3. Codes of View
<h3>Login Page</h3>
#ViewBag.error
<form method="post" asp-controller="account" asp-action="login">
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td>Username</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="Login"></td>
</tr>
</table>
</form>
4. Codes of returning View when success
#using Microsoft.AspNetCore.Http;
<h3>Success Page</h3>
Welcome #Context.Session.GetString("username")
<br>
<a asp-controller="account" asp-action="logout">Logout</a>
Test result

RuntimeBinderException: Cannot perform runtime binding on a null reference

I'm making a create item page, and in this create item page there is a popup modal table where we can choose the type of UoM that we want. And normally when this form is submitted with all of the fields filled in, it saved the values into the database. But when the form is submitted with one or some or all of the fields not filled in, it supposed to give some error message that the fields are required. But it didn't and it shows this error.
These are my code
ItemController
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Threading.Tasks;
using CRMandOMS.Models;
using CRMandOMS.ViewModels;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
// For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace CRMandOMS.Controllers
{
public class ItemController : Controller
{
private readonly IItemRepository _itemRepository;
private readonly IUoMRepository _uoMRepository;
public ItemController(IItemRepository itemRepository, IUoMRepository uoMRepository)
{
_itemRepository = itemRepository;
_uoMRepository = uoMRepository;
}
// GET: /<controller>/
public ViewResult Index()
{
var model = _itemRepository.GetAll();
return View(model);
}
public ViewResult Details(Guid? id)
{
Item item = _itemRepository.GetById(id.Value);
return View(item);
}
[HttpGet]
public ViewResult Create()
{
ItemCreateViewModel itemCreateViewModel = new ItemCreateViewModel()
{
UoMs = _uoMRepository.GetAll()
};
return View(itemCreateViewModel);
}
[HttpPost]
public IActionResult Create(ItemCreateViewModel model)
{
if (ModelState.IsValid)
{
Item newItem = new Item
{
Name = model.Name,
Price = model.Price,
UoMId = model.UoMId
};
_itemRepository.Insert(newItem);
return RedirectToAction("Details", new { id = newItem.Id });
}
return View();
}
}
}
Create
#model CRMandOMS.ViewModels.ItemCreateViewModel
#{
ViewData["Title"] = "Item Create";
}
<h2>Item Create</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a asp-controller="Item" asp-action="Index">Item</a></li>
<li class="breadcrumb-item active" aria-current="page">Create</li>
</ol>
</nav>
<form enctype="multipart/form-data" asp-controller="Item" asp-action="Create" method="post" class="mt-3">
<div class="form-group row">
<label asp-for="Name" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Name" class="form-control" placeholder="Name" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="Price" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Price" class="form-control" placeholder="Price" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="UoMId" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="UoMId" id="uomid" class="form-control" hidden />
<div class="input-group mb-3">
<input id="uomname" type="text" class="form-control" placeholder="UoM" aria-label="UoM" aria-describedby="button-uom" disabled>
<div class="input-group-append">
<button class="btn btn-outline-success" type="button" id="button-uom" data-toggle="modal" data-target="#uoMLookupTableModal">Select UoM</button>
</div>
</div>
<span asp-validation-for="UoMId" class="text-danger"></span>
</div>
</div>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group row">
<div class="col-sm-2"></div>
<div class="col-sm-10">
<a asp-controller="Item" asp-action="Index" class="btn btn-light">Back</a>
<button type="submit" class="btn btn-success">Create</button>
</div>
</div>
</form>
#{
await Html.RenderPartialAsync("_UoMLookup");
}
#section scripts {
<script>
$(document).ready(function () {
var uoMTable = $("#uoMTable").DataTable({
"columnDefs": [
{
"targets": [0],
"visible": false
}
],
"order": [[1, "asc"]]
});
$('#uoMTable tbody').on('click', 'tr', function () {
if ($(this).hasClass('table-success')) {
$(this).removeClass('table-success');
}
else {
uoMTable.$('tr.table-success').removeClass('table-success');
$(this).addClass('table-success');
}
});
$("#getUoM").click(function () {
var uomdata = uoMTable.row('.table-success').data();
//alert(uomdata[0]);
$('#uomid').val(uomdata[0]);
//alert(uomdata[1]);
$('#uomname').val(uomdata[1]);
});
});
</script>
}
_UoMLookup
<div class="modal fade" id="uoMLookupTableModal" tabindex="-1" role="dialog" aria-labelledby="uoMLookupTableModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<table id="uoMTable" class="table table-striped table-bordered table-bordered nowrap" style="width:100%">
<thead>
<tr>
<td>Id</td>
<td>Name</td>
<td>Description</td>
</tr>
</thead>
<tbody>
#foreach (UoM uom in Model.UoMs)
{
<tr>
<td class="uom-id">#uom.Id</td>
<td class="uom-name">#uom.Name</td>
<td>#uom.Description</td>
</tr>
}
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-dismiss="modal">Cancel</button>
<button id="getUoM" type="button" class="btn btn-success" data-dismiss="modal">Select</button>
</div>
</div>
</div>
</div>
ItemCreateViewModel
using CRMandOMS.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace CRMandOMS.ViewModels
{
public class ItemCreateViewModel
{
[Required]
[MaxLength(100, ErrorMessage = "Name cannot exceed 100 characters")]
public string Name { get; set; }
[Required(ErrorMessage = "{0} is required")]
[Range(1000, 999999999)]
public int Price { get; set; }
[Required]
public Guid UoMId { get; set; }
public IEnumerable<UoM> UoMs { get; set; }
public string PhotoPath { get; set; }
}
}
In the HTTP POST Create method (ItemController) if the model is not valid (so ModelState.IsValid == false) you are not passing a model to your View. Ensure passing a valid model, as shown in the controller methods tutorial.
But when the form is submitted with one or some or all of the fields not filled in, it supposed to give some error message that the fields are required. But it didn't and it shows this error.
You do not have a reference to validation scripts, make sure you have _ValidationScriptsPartial.cshtml in Shared folder, then modify your code:
#section scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
<script>
//...
</script>
}
For the error on your page, just like other community has said, it is likely that the model state is invalid and it execute return View() without returning any data to create view.
However,your partial view does not allow the Model.UoMs to be null.
In your Create Post action, if the model contains UoMs, you could just use
return View(model)
otherwise ,assign UoMs data to model like what you have done in Create Get action, then return it to view.
You could always use a breakpoint on the Post action to debug the result.

Sending loaded object to view and getting back changes from POST using Razor Pages

After performing a table query, loading up an object with properties, and sending it back to the view, I'd like to update some of the properties and post it back to the Azure table.
I have a model binding in my Razor Page cs file as follows:
[BindProperty]
public TenantSettingsModel Settings { get; set; }
In my OnGetAsync method I load up the Settings object as follows:
public async Task<IActionResult> OnGetAsync()
{
Settings = _azureTableConn.GetTenantSettings(passSomeValue);
return Page();
}
At this stage I can see Settings is loaded with the properties to return to the view. I'm only picking out a couple of the properties to display in a form and I'd like to post back those two changes (if any) along with the other unchanged properties to the OnPostAsync method as follows and render a view:
public async Task<IActionResult> OnPostAsync()
{
var response = _azureTableconn.SetTenantSettings(Settings);
return Page();
}
When I inspect the Settings object it has the two form properties but the others are null. I could simply set them manually but it seems like a waste.
Is it possible to send an object with pre-defined properties, then change two of them in a form, but retain the unchanged properties when it comes back from the view?
My only other thought is to create hidden form elements to hold the [in]bound properties from the model but if I have to do this I'm better off setting it in my OnPostAsync method I think.
I have not seen your pages or code but I just made some postings to CodeProject that may help. It appears you may need to use a form element in your .cshtml file. Your [BindProperty] attribute would make your TenantSettingsModel values available to the elements in your UI. You might look at (ASP.NET Core Razor Pages Using EntityFramework Core with Enums as Strings - Part II)
I am using Entity Framework Core here.
In one example I edit a Customer object on a Razor page, Edit.cshtml ([Note: I do use a hidden input to pass the Customer.CustomerId to the PostAsync() method]):
#page
#model QuantumWeb.Pages.Customers.EditModel
#{
ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<h4>Customer</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Customer.CustomerId" />
<div class="form-group">
<label asp-for="Customer.CustomerName" class="control-label"></label>
<input asp-for="Customer.CustomerName" class="form-control" />
<span asp-validation-for="Customer.CustomerName"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Customer.CustomerContact"
class="control-label"></label>
<input asp-for="Customer.CustomerContact" class="form-control" />
<span asp-validation-for="Customer.CustomerContact"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Customer.CustomerPhone"
class="control-label"></label>
<input asp-for="Customer.CustomerPhone" class="form-control" />
<span asp-validation-for="Customer.CustomerPhone"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Customer.CustomerEmail"
class="control-label"></label>
<input asp-for="Customer.CustomerEmail" class="form-control" />
<span asp-validation-for="Customer.CustomerEmail"
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-page="./Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
The "code-behind", Edit.cshtml.cs:
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Data;
using QuantumWeb.Model;
namespace QuantumWeb.Pages.Customers
{
public class EditModel : PageModel
{
private readonly QuantumDbContext _context;
public EditModel(QuantumDbContext context)
{
_context = context;
} // end public EditModel(QuantumDbContext context)
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
} // endif (id == null)
Customer =
await _context.Customers
.FirstOrDefaultAsync(m => m.CustomerId == id);
if (Customer == null)
{
return NotFound();
} // endif (Customer == null)
return Page();
} // end public async Task<IActionResult> OnGetAsync(int? id)
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
} // endif (!ModelState.IsValid)
_context.Attach(Customer).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
} // end try
catch (DbUpdateConcurrencyException)
{
if (!CustomerExists(Customer.CustomerId))
{
return NotFound();
}
else
{
throw;
} // endif (!CustomerExists(Customer.CustomerId))
} // end catch (DbUpdateConcurrencyException)
return RedirectToPage("./Index");
} // end public async Task<IActionResult> OnPostAsync()
private bool CustomerExists(int id)
{
return _context.Customers.Any(e => e.CustomerId == id);
} // end private bool CustomerExists(int id)
} // end public class EditModel : PageModel
} // end namespace QuantumWeb.Pages.Customers
I hope this helps.
I have other parts in my CodeProject post (Parts III & IV) that use Entity Framework eager loading to load related data in the GET. ( [ASP.NET Core Razor Pages Using EntityFramework Core with Enums as Strings - Part III and ASP.NET Core Razor Pages Using EntityFramework Core with Enums as Strings - Part IV)
In Part IV I have a page, CustomerProjectSkillAssign.cshtml:
#page "{id:int?}"
#model QuantumWeb.Pages.Customers.CustomerProjectSkillAssignModel
#{
ViewData["Title"] = "Customer Project Skill Assignment";
}
<h2>Customer Project Skill Assignment</h2>
<hr />
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => model.Project.ProjectId)
</dt>
<dd>
#Html.DisplayFor(model => model.Project.ProjectId)
</dd>
<dt>
#Html.DisplayNameFor(model => model.Project.ProjectName)
</dt>
<dd>
#Html.DisplayFor(model => model.Project.ProjectName)
</dd>
<dt>
#Html.DisplayNameFor(model => model.Customer.CustomerId)
</dt>
<dd>
#Html.DisplayFor(model => model.Customer.CustomerId)
</dd>
<dt>
#Html.DisplayNameFor(model => model.Customer.CustomerName)
</dt>
<dd>
#Html.DisplayFor(model => model.Customer.CustomerName)
</dd>
</dl>
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Project.ProjectId" />
<div class="form-group">
<label asp-for="SkillTitle.SkillCode" class="control-label"></label>
<select asp-for="SkillTitle.SkillCode" class="form-control" asp-items="ViewBag.SkillCode"></select>
</div>
<div class="form-group">
<a asp-page="./CustomerProjectSkills" asp-route-id="Project.ProjectId">Project Skills</a> |
<input type="submit" value="Assign" class="btn btn-default" />
</div>
</form>
</div>
</div>
Notice I have only one hidden control that has the Project.ProjectId
The CustomerProjectSkillAssign.cshtml.cs:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Data;
using QuantumWeb.Model;
namespace QuantumWeb.Pages.Customers
{
public class CustomerProjectSkillAssignModel : PageModel
{
private readonly QuantumDbContext _context;
public CustomerProjectSkillAssignModel(QuantumDbContext context)
{
_context = context;
} // end public CustomerProjectSkillAssignModel(QuantumDbContext context)
[BindProperty]
public Project Project { get; set; }
[BindProperty]
public Customer Customer { get; set; }
[BindProperty]
public SkillTitle SkillTitle { get; set; }
public async Task<IActionResult> OnGet(int? id)
{
if (id == null)
{
return NotFound();
} // endif (id == null)
Project = await _context.Projects
.Include(p => p.Customer)
.Include(p => p.ProjectSkills)
.ThenInclude(ps => ps.SkillTitle)
.FirstOrDefaultAsync(p => p.ProjectId == id);
if (Project == null)
{
return NotFound();
} // endif (Project == null)
Customer = Project.Customer;
ViewData["SkillCode"] = new SelectList(_context.SkillTitles, "SkillCode", "Title");
return Page();
}// end public async Task<IActionResult> OnGet(int? id)
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
} // endif (!ModelState.IsValid)
ProjectSkill projectSkill = new ProjectSkill()
{
ProjectId = Project.ProjectId,
SkillCode = SkillTitle.SkillCode
};
_context.ProjectSkills.Add(projectSkill);
await _context.SaveChangesAsync();
return RedirectToPage("./CustomerProjectSkills", new { id = Project.ProjectId });
} // end public async Task<IActionResult> OnPostAsync()
} // end public class CustomerProjectSkillAssignModel : PageModel
} // end namespace QuantumWeb.Pages.Customers
The eager loading retrieves the Project object and its Customer object and its collection of ProjectSkills. Each ProjectSkill has a SkillTitle. In addition I load a collection of available SkillTitle objects into a select/dropdown. The selected item is passed to OnPostAsync() via the form. However, there is only one hidden control to indicate the target Project.
I have a similar challenge and not sure if my approach is right. Instead of displaying the data (to be edited) in DIV, I'm displaying in a TABLE. I can understand how the hidden property is updating the row. In my case I am displaying multiple rows on the Edit page, but unable to update the database for only rows which have updates.
My view page is like this
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<table class="table">
<thead>
<tr>
<th> #Html.DisplayNameFor(model => model.Fields[0].Name) </th>
<th> #Html.DisplayNameFor(model => model.Fields[0].Value) </th>
<th> #Html.DisplayNameFor(model => model.Fields[0].Confidence) </th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Fields)
{
<tr>
<td> <input type="hidden" asp-for=#item.ID /> </td>
<td> #Html.DisplayFor(modelItem => item.Name) </td>
<td contenteditable='true'> #Html.DisplayFor(modelItem => item.Value) </td>
<td> #Html.DisplayFor(modelItem => item.Confidence) </td>
</tr>
}
</tbody>
</table>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
And the code behind is this. The Fields is showing 0 in the OnPostAsync method.
[BindProperty]
public IList<FieldInfo> Fields { get; set; }
public async Task<IActionResult> OnGetAsync(string id)
{
if (id == null)
{
return NotFound();
}
Fields = await _context.FieldInfo.Where(m => m.GUID == id).ToListAsync();
if (Fields == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
foreach (var p in Fields) // In this approach Fields shows as count=0
{
_context.Attach(p.Value).State = EntityState.Modified;
}
_context.Attach(Fields).State = EntityState.Modified; // In this approach Exception: The entity type 'List<FieldInfo>' was not found.
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}

Categories

Resources