I want to create razor pages that perform CRUD operations on files in a sql database. I managed to upload files to the database using IFormFile and MemoryStream but I am not able to update/replace them in the same way. When I select the new file and click on Save, I get a message that no file was selected.
I tried the following code
File.cs
namespace MyApp.Models
{
public class File
{
public string Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public byte[]? Content { get; set; }
}
}
Edit.cshtml
#page
#model MyApp.Pages.Files.EditModel
#{
ViewData["Title"] = "Edit";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<h1>Edit</h1>
<h4>File</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="File.Id" />
<div class="form-group">
<label asp-for="File.Title" class="control-label"></label>
<input asp-for="File.Title" class="form-control" />
<span asp-validation-for="File.Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="File.Description" class="control-label"></label>
<input asp-for="File.Description" class="form-control" />
<span asp-validation-for="File.Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FileUpload.FormFile"></label>
<input asp-for="FileUpload.FormFile" type="file">
<span asp-validation-for="FileUpload.FormFile"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="./Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Edit.cshtml.cs
#nullable disable
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using MyApp.Data;
using MyApp.Models;
namespace MyApp.Pages.Files
{
public class EditModel : PageModel
{
private readonly ApplicationDbContext _context;
public EditModel(ApplicationDbContext context)
{
_context = context;
}
[BindProperty]
public File File { get; set; }
[BindProperty]
public BufferedSingleFileUploadDb FileUpload { get; set; }
public class BufferedSingleFileUploadDb
{
[Required]
[Display(Name = "File")]
public IFormFile FormFile { get; set; }
}
public async Task<IActionResult> OnGetAsync(string id)
{
if (id == null)
{
return NotFound();
}
File = await _context.File.FirstOrDefaultAsync(m => m.Id == id);
if (File == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
using (var memoryStream = new MemoryStream())
{
await FileUpload.FormFile.CopyToAsync(memoryStream);
// Upload the file if less than 2 MB
if (memoryStream.Length < 2097152)
{
File.Content = memoryStream.ToArray();
}
else
{
ModelState.AddModelError("File", "The file is too large.");
}
}
_context.Attach(File).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!FileExists(File.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool FileExists(string id)
{
return _context.File.Any(e => e.Id == id);
}
}
}
Add enctype="multipart/form-data" to your form element. –
Mike Brind
I have a list of CheckModel classes with properties int Id and bool IsChecked. I want to filter them based on the IsChecked property.
When I render them in a foreach loop filtering the already checked items, I get buggy behavior. The item is removed from the view, but the item below that takes it's place in the view renders as checked, while in fact it is not.
Here is a gif showing this behavior:
It seems that Blazor's rendering somehow lags behind with the checkboxes..
Here is the code:
#page "/"
<div>
<input id="filter-collected-checkbox" type="checkbox" #bind="FilterChecked" />
<label for="filter-collected-checkbox">Filter</label>
</div>
#foreach((CheckModel item, int index) in CheckModels.Where(x=>!FilterChecked || !x.IsChecked).Select((x,i)=>(x,i)))
{
<div style="display: flex">
#item.Id
<input id="item-collected-checkbox-#index" type="checkbox" checked="#item.IsChecked" #onchange="(e)=>MarkItemCollected(e,item)"/>
</div>
}
#code {
public List<CheckModel> CheckModels { get; set; }
public bool FilterChecked { get; set; }
protected override void OnInitialized()
{
CheckModels = new List<CheckModel>();
for (int i = 0; i < 10; i++)
{
CheckModels.Add(new CheckModel() { Id = i });
}
}
private void MarkItemCollected(ChangeEventArgs e, CheckModel item)
{
item.IsChecked = (bool)e.Value;
}
}
The reason why I'm using html checked-attribute with #onchange is because I want to have a method after the binding has occurred. If I use #bind=IsChecked with #onclick=Method, the #onclick is fired before the binding.
Anyone know how to fix this?
You need to use the #key for the loop contents so the Render engine knows what items need updating.
<div #key=#item.Id style="display: flex">
Docs are here
Working REPL
FYI:
<div>
<input id="filter-collected-checkbox" type="checkbox" #bind="filterChecked" />
<label for="filter-collected-checkbox">Filter</label>
</div>
#foreach(var item in FilteredItems)
{
<div #key=#item.Id style="display: flex">
#item.Id
<input id="item-collected-checkbox-#item.Id" type="checkbox" #bind="#item.IsChecked" />
</div>
}
#code {
List<CheckModel> checkModels = Enumerable.Range(0,10)
.Select(i => new CheckModel() { Id = i })
.ToList();
bool filterChecked;
IEnumerable<CheckModel> FilteredItems =>
filterChecked ? checkModels.Where(x=> !x.IsChecked) : checkModels;
}
Renders the same result.
I have a view that has 2 option for edit and create form on controller.
Now I want to control view for edit and create from.
It is about show of city and state.
If form was create I want to show a select tag for shoe location Id
If form was edited I want to show a select tag for both city Id and State Id
Now I want to know how can I control it in view. Create and edit work fine but when I want check it separate I have problem
These are my view and controller:
View
State
<select id="Location_Id" class="form-control">
#if (state.Any())
{
#foreach (var item in state)
{
<option value="#item.LocationId">#item.Title</option>
}
}
</select>
<span asp-validation-for="LocationId"></span>
<label>City</label>
<select asp-for="LocationId" id="SubLocation_Id" class="form-control ">
#foreach (var item in city)
{
<option value="#item.LocationId ">#item.Title</option>
}
</select>
<span asp-validation-for="LocationId"></span>
</label>
If was edited change to :
<label class=" w-100 d-flex align-items-center" for="">
<label>State </label>
<select id="Location_Id" class="form-control">
#if (state.Any())
{
#foreach (var item in state)
{
<option value="#item.LocationId">#item.Title</option>
}
}
</select>
<span asp-validation-for="StateId"></span>
<label> City </label>
<select asp-for="CityId" id="SubLocation_Id" class="form-control">
#foreach (var item in city)
{
<option value="#item.LocationId ">#item.Title</option>
}
</select>
<span asp-validation-for="CityId"></span>
</label>
And this is my controller:(post method)
[HttpPost("userInfo/first-start-userInfo/{packageId}"), ValidateAntiForgeryToken]
public async Task<IActionResult> UpsertStartUserInfo(UpsertStartUserViewModel createStartUser)
{
var userId = User.GetCurrentUserId();
var res = await _packageService.UpsertUserInfoByUser(createStartUser, userId);
return View(createStartUser);
}
and this is my view model:
public long PackageId { get; set; }
public string Address { get; set; }
public Sex Sex { get; set; }
public Marriage Marriage { get; set; }
public string Goal { get; set; }
public long? LocationId { get; set; }
public long? CityId { get; set; }
public long? StateId { get; set; }
I am trying to persist the information in the 'auction' variable, to the table 'Auction', which has not been created yet. Entity framework is supposed to create it for me I understand. As you see, my program has made it to the point where the form data is contained in the 'auction' variable. But as you can see the debugger is stopped on 'db.Auction.Add(auction)'.
Why won't the program proceed by letting the db add the data in the 'auction' variable, to the Auction table?
I'd appreciate it.
Thanks,
CM
Thank you for replying so far but the suggestions are not working. I've written in my code as well as shown the error message again which is the same message I had that started this thread.
The View
#model MvcAuction.Models.Auction
#{
ViewBag.Title = "CreateAuctionItem";
}
<h2>#ViewBag.Title.</h2>
<div id="createAuctionItemSection">
#using (Html.BeginForm("Create", "Auctions", FormMethod.Post,
new { #class = "form-horizontal", #id =
"registerForm", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Create An Item For Auction.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.Title, new { #class = "col-md-2 control-
label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Title, new { #class = "form-control", #id = "title" })
</div>
</div>
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.StartDate, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.StartDate, "{0:yyyy-MM-dd}", new { type = "date" })
</div>
</div>
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.EndDate, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.EndDate, "{0:yyyy-MM-dd}", new { type = "date" })
</div>
</div>
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.DeliveryCost, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.DeliveryCost, new { #class = "form-control", #id = "deliveryCost" })
</div>
</div>
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.StartBid, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.StartBid, new { #class = "form-control", #id = "startBid" })
</div>
</div>
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.BuyNowPrice, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.BuyNowPrice, new { #class = "form-control", #id = "buyNowPrice" })
</div>
</div>
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.BuyNowEnabled, new { #Value = "Show Buy Now Price?", #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.CheckBoxFor(m => m.BuyNowEnabled, new { #class = "form-control", #id = "buyNowEnabled" })
</div>
</div>
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.Description, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Description, new { #class = "form-control", #id = "description" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Create Item" />
</div>
</div>
}
<img src="~/Content/Images/progress.gif" id="progress" style="display:none;" />
<h3>#ViewBag.TheMessage</h3>
</div><!--End createAuctionItemSection-->
The Model
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;
namespace MvcAuction.Models
{
public class Auction
{
public long Id { get; set; }
[Required]
[Column(TypeName = "varchar")]
[Display(Name = "Title")]
public String Title { get; set; }
public string ImageURL { get; set; }
[Required]
[Column(TypeName = "date")]
[Display(Name = "Start Date")]
public DateTime StartDate { get; set; }
[Required]
[Column(TypeName = "date")]
[Display(Name = "End Date")]
public DateTime EndDate { get; set; }
[Required]
[Column(TypeName = "decimal")]
[Display(Name = "Delivery Cost")]
public decimal DeliveryCost { get; set; }
[Required]
[Column(TypeName = "decimal")]
[Display(Name = "Start Bid")]
public decimal StartBid { get; set; }
[Column(TypeName = "decimal")]
[Display(Name = "Buy Now Price")]
public decimal BuyNowPrice { get; set; }
[Column(TypeName = "bool")]
[Display(Name = "Buy Now Enabled")]
public Boolean BuyNowEnabled { get; set; }
[Column(TypeName = "varchar")]
[Display(Name = "Description")]
public String Description { get; set; }
[Column(TypeName = "int")]
[Display(Name = "View Count")]
public int ViewCount = 0;
public decimal? getCurrentTopBid()
{
return StartBid;
}
}
}
The Controller Action
[HttpPost]
public ActionResult Create(Auction auction )
{
if (ModelState.IsValid)
{
var db = new AuctionsDataContext();
db.Auction.Add(auction);
db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
Action is already apart of the DbContext class as a DbSet. Also the view already sends the Model information back you don't need that as an overloaded parameter. Change your create post to
public class AuctionsController : Controller
{
private readonly AuctionsDataContext_context;
public AuctionsController(AuctionsDataContext context)
{
_context = context;
}
public ActionResult Create ( Bind[("Id,Title,StartDate,EndDate,DeliveryCost,StartBid,BuyNowPrice,BuyNowEnabled,Description")] Auction auction)
{
if (ModelState.IsValid)
{
_context.Add(auction);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View();
}
I'm new to asp.net and MVC, and I have a problem.
I know that this is something simple but I do not know how to do it. I seek advice and would like to thank you in advance for any help.
This is my problem:
I have 2 tables: table X: ID (primary key), Number; and table Y: ID (primary key), NID (foreign key with relationship with table X), etc.
What I want to know is how to display last inserted ID into the view of table Y on an Html editor for NID the last value of ID (table X)?
For example, I create a new row in table X, and when I want to create the row in table Y that corresponds with table X to automatically get the last ID inserted in the textbox or editor?
Can anybody give me some kind of reference or an example! Thank you for your help! Sorry for any bad spelling.
Here we go . I tested this and it returned me the model properties along with files posted . This example gives you ideea how POSt method used in MVC and how to send model propertied back to controller .
//-- this is the controller
public class FileUploadDemoController : Controller
{
//
// GET: /FileUploadDemo/
public ActionResult Index()
{
// here find the last if of the FileUploadtable
var ctx = new TestDbContext();
var maxId = ctx.Fileuploads.ToList().OrderByDescending(u => u.Id).FirstOrDefault();
var newId = maxId == null ? 1 : maxId.Id + 1;
return View("Index", new FileUploadModel { Id= newId });
}
[HttpPost]
public ActionResult PostForm(FileUploadModel model)
{
// here you have NewId in model.Id method ; Now ypour table b in my case is fileeuploadhistory I want to insert a new record with this model.Id
using (var ctx = new TestDbContext())
{
var curretFile = ctx.Fileuploads.FirstOrDefault(x => x.Id == model.Id);
if (curretFile==null)
{
curretFile=new FileUploadModel { Name=model.Name , ValidFromDate= model.ValidFromDate};
}
curretFile.History = new FileUploadHistory { InsertedDate = DateTime.Now };
ctx.Fileuploads.Add(curretFile);
ctx.SaveChanges();
}
return View("Index", model);
}
}
-- These are MY EntityFramework entities and I am using same on Views as well
public class FileUploadModel
{
public FileUploadModel()
{
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string ValidFromDate { get; set; }
public int HistoryId { get; set; }
[ForeignKeyAttribute("HistoryId")]
public virtual FileUploadHistory History { get; set; }
}
public class FileUploadHistory
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime InsertedDate { get; set; }
}
-- Finaly the cshml file . The import point is to use new { enctype = "multipart/form-data" } inside BeginForm . // the page from where you will post the data . Please change you model class in place of FileUploadModel I created for me .
#model WebApplication1.Models.FileUploadModel
#using (Html.BeginForm("PostForm", "FileUploadDemo", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="panel">
<div class="panel-body">
<div class="form-group row">
<div class="col-md-2 form-label">
<label>ID:</label>
</div>
<div class="col-md-6">
#Html.TextAreaFor(x => x.Id , new { #class = "form-control" })
</div>
</div>
<div class="form-group row">
<div class="col-md-2 form-label">
<label>Name:</label>
</div>
<div class="col-md-6">
#Html.TextAreaFor(x => x.Name, new { #class = "form-control" })
</div>
</div>
<div class="form-group row">
<div class="col-md-2 form-label">
<label>Date</label>
</div>
<div class="col-md-6">
#Html.TextAreaFor(x => x.ValidFromDate, new { #class = "form-control" })
</div>
</div>
<div class="col-md-10">
<div class="form-group row">
<div class="col-md-2 form-label">
<label>Select File<i class="required-field">*</i>:</label>
</div>
<div class="col-md-8">
<input type="file" class="file-upload" style="margin: 0px;" hidden="hidden" accept=".xlsx" name="file" id="file" />
</div>
</div>
</div>
<div class="form-group row">
<div class="col-md-3 pull-right text-right">
<button class="btn btn-primary" id="process-submission" type="submit">
Submit
</button>
</div>
</div>
</div>
</div>
}