mvc 5 update bind attribute in submit - c#

I have an MVC 5 with Entity Framework application, database first and I am new to MVC. I have a file picker control that selects an image and in Action Result class of the create view, I encode the picture for storage in SQL. I have the encoding correct, but the data from the view is in the Bind attributes and I want to update the property from the Bind with encoded picture before I write it to the database. When I try to write to the database, the whole app simply churns until it runs out of memory. Obviously, I am missing a piece.
View: This is the div with the filepicker and the place where the Entity Framework wanted the coded pic to go.
<div class="col-md-10"><br/><br/>
#Html.TextBox("beforeEncode", "", new { type = "file" })<br />
#Html.LabelFor(model => model.fldPhoto, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.fldPhoto, new { htmlAttributes = new { #class = "form-control"} })
#Html.ValidationMessageFor(model => model.fldPhoto, "", new { #class = "text-danger" })
</div>
</div>
Controller: This is the controller with the irrelevant parts deleted. I have a breakpoint set on the db.SaveChanges(); line for checking if there was an issue with the encoding, but even with it removed, it just churns
public ActionResult Create([Bind(Include = "fldPhotoID,fldPhoto,beforeEncode")] tblPhoto tblPhoto, String beforeEncode)
{
var fileupload = beforeEncode;
try
{
if (ModelState.IsValid)
{
if (fileupload != null)
{
FileStream fs;
fs = new FileStream(beforeEncode, FileMode.Open, FileAccess.Read);
byte[] PicEncoded = new byte[fs.Length];
fs.Read(PicEncoded, 0, Convert.ToInt32(fs.Length));
fs.Close();
tblPhoto.fldPhoto = PicEncoded;
}
db.tblPhotoes.Add(tblPhoto);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}

Related

HttpPostedFileBase Null in controller - Want to save file path to database

I want to save the file path to my database reports table. I have a column of type: string FilePath.
The end goal is that I want to be able to download the file from a report details view. Obviously the report download link would be different depending on the report ID.
Currently it doesn't seem that the controller is receiving anything as before I had Object reference not set to an instance of an object exception. I then added file != null in my if statement so I don't get the error anymore. However clearly the underlying issue is still present. Here is my controller save action:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "AdminManager")]
public ActionResult Save(Report report, HttpPostedFileBase file)
{
if (!ModelState.IsValid)
{
var viewModel = new ReportFormViewModel
{
Report = report,
Members = _context.Members.ToList(),
Subjects = _context.Subjects.ToList()
};
return View("ReportForm", viewModel);
}
if (file != null && file.ContentLength > 0)
{
string filePath = Path.Combine(
Server.MapPath("~/App_Data/Uploads"),
Path.GetFileName(file.FileName));
file.SaveAs(filePath);
}
if (report.Id == 0)
_context.Reports.Add(report);
else
{
var reportInDb = _context.Reports.Single(e => e.Id == report.Id);
reportInDb.Name = report.Name;
reportInDb.MemberId = report.MemberId;
reportInDb.SubjectId = report.SubjectId;
reportInDb.Date = report.Date;
reportInDb.FilePath = report.FilePath;
}
_context.SaveChanges();
return RedirectToAction("Index", "Report");
}
Here is my form view:
<h2>#Model.Title</h2>
#using (Html.BeginForm("Save", "Report", new {enctype = "multipart/form-data"}))
{
<div class="form-group">
#Html.LabelFor(r => r.Report.Name)
#Html.TextBoxFor(r => r.Report.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(r => r.Report.Name)
</div>
<div class="form-group">
#Html.LabelFor(r => r.Report.Date) e.g. 01 Jan 2000
#Html.TextBoxFor(r => r.Report.Date, "{0: d MMM yyyy}", new { #class = "form-control" })
#Html.ValidationMessageFor(r => r.Report.Date)
</div>
<div class="form-group">
#Html.LabelFor(m => m.Report.MemberId)
#Html.DropDownListFor(m => m.Report.MemberId, new SelectList(Model.Members, "Id", "Name"), "Select Author", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Report.MemberId)
</div>
<div class="form-group">
#Html.LabelFor(m => m.Report.SubjectId)
#Html.DropDownListFor(m => m.Report.SubjectId, new SelectList(Model.Subjects, "Id", "Name"), "Select Subject", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Report.SubjectId)
</div>
<div class="form-group">
#Html.LabelFor(m => m.Report.FilePath)
<input type="file" name="file" id="file"/>
</div>
#Html.HiddenFor((m => m.Report.Id))
#Html.AntiForgeryToken()
<button type="submit" class="btn btn-primary">Save</button>
}
Current code doesn't seem to send file data to action.
It is recommended to add the file to your model:
public class Report {
[Required]
[Display(Name = "Report File")]
public HttpPostedFileBase ReportFile { get; set; }
//... The other fields
}
Usually I would append ViewModel, so ReportViewModel instead of Report. This makes it easier to distinguish between view models and business/data models.
And in your Razor:
<div class="form-group">
#Html.LabelFor(m => m.Report.ReportFile)
#Html.TextBoxFor(m => m.ReportFile, new { type = "file" })
<!--You can also use <input type="file" name="ReportFile" id="ReportFile"/>-->
</div>
Note that the name that you use in the LabelFor must match the ID of the control. In your code FilePath and file didn't match.
And finally in the controller:
public ActionResult Save(Report report)
{
//...some code
string filePath = Path.Combine(Server.MapPath("~/App_Data/Uploads"),
Path.GetFileName(report.ReportFile.FileName));
report.ReportFile.SaveAs(filePath);
//...other code
}
I wouldn't use the name of the uploaded file. Instead, I would give it a name according to my project's naming convention. I often use the ID as the name, perhaps with some prefix. Example:
var fileName = "F" + report.Id + ".jpg"; //You can get the extension from the uploaded file
string filePath = Path.Combine(Server.MapPath("~/App_Data/Uploads"), fileName);
Obviously, when you're inserting a new object, you won't have an ID until you insert it into the database, so the code to save the physical file must be placed after the code to insert it into the database. If you follow this logic, you don't need to save the path in the database, because the path can be always calculated from the ID. So you save a column in the database, gain performance in your code as you don't need to handle another string column, and you have a clear and simply file naming convention that is safe without user input risk.
Another way I follow, especially when the type of the file may vary (i.e. you can upload files with different extensions), is using a GUID for the file name. In this case, the file name must be saved in the database, but the GUID can be generated before inserting the object into the database. Example:
string ext = report.ReportFile.FileName.Substring(
report.ReportFile.FileName.LastIndexOf('.')).ToLower();
var fileName = Guid.NewGuid().ToString() + ext;
string filePath = Path.Combine(Server.MapPath("~/App_Data/Uploads"), fileName);

How to correctly recover date from SQL Server?

I created a simple CRUD using ASP.NET MVC and I can save my registers normally.
If I look in the Data Base my date was stored like that:
2017-06-01 00:01:23.750
But when I try to Edit the same register whith an async method created by MVC Scaffolding the date appears to bel null:
01/01/0001 00:00:00
Then (of course) I get an error:
The conversion of a datetime2 data type to a datetime data type
resulted in an out-of-range value.
Thats the async method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "ArtigoId,Titulo,Texto,DataPublicacao,UltimaModificacao,UsuarioModificacao,DataCriacao,UsuarioCriacao")] Artigo artigo)
{
if (ModelState.IsValid)
{
db.Entry(artigo).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(artigo);
}
Why does it happen? How to correctly recover the date from data base?
EDIT:
I changed in Data Base for datetime2(0-7) but that made it accept the value 01/01/0001 00:00:00 and what I want is to know why this value appears instead of 2017-06-01 00:01:23.750.
All the others values in the data base is recovered normally, but not this date.
The problem occurred because I removed my field in the View and there is nothing to do with the date format in SQL Server.
I put the field back in the view as readyonly:
<div class="form-group">
#Html.LabelFor(model => model.DataCriacao, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DataCriacao, new { htmlAttributes = new { #class = "form-control", #readonly = "readonly" } })
#Html.ValidationMessageFor(model => model.DataCriacao, "", new { #class = "text-danger" })
</div>
</div>
The search of the field in the DB is done in the GET method:
// GET: Artigos/Edit/5
public async Task<ActionResult> Edit(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Artigo artigo = await db.Artigoes.FindAsync(id);
if (artigo == null)
{
return HttpNotFound();
}
return View(artigo);
}
The method I posted was POST after the submit of my button to save the changes and no searches were done on the database.

ASP.net use session data to add something in create view

I created this simple ASP.NET project. (Default template using MVC)
In there I generated models from my database using ADO.net
I also generated controller for my model. (The model generated functions for create, edit, delete...) I also got view for every function in the controller.
So what I am trying to do now is:
-I am in my create view. (that means I see my form for creating objects)
-I need to enter data for [title, content] but to post in database I also need an id (this id is a foreign key, not the id of the object i am creating)
I already have this id saved in my session. I can access the session data by doing:
var user = Session["user"] as Uporabniki; //returns session data
user.id //selects id from session
Now what I want is to use this id in the create form textbox.
As of now the rows for id in my view look like this (I have no idea why it's a dropdown list. When I open the site I see names of all users in database and I can select one. But this is not what I want. I want to see only one):
<div class="form-group">
#Html.LabelFor(model => model.id_avtorja, "id_avtorja", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("id_avtorja", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.id_avtorja, "", new { #class = "text-danger" })
</div>
</div>
The create methods in controller look like this
// GET: Vprasanjas/Create
public ActionResult Create()
{
ViewBag.id_avtorja = new SelectList(db.Uporabniki, "id", "uporabniskoIme");
return View();
}
// POST: Vprasanjas/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,naslov,vsebina,datum,id_avtorja")] Vprasanja vprasanja)
{
if (ModelState.IsValid)
{
db.Vprasanja.Add(vprasanja);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.id_avtorja = new SelectList(db.Uporabniki, "id", "uporabniskoIme", vprasanja.id_avtorja);
return View(vprasanja);
}
Why is it not working if I change the view to something like this:
<div class="form-group">
#Html.LabelFor(model => model.id_avtorja, "id_avtorja", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#var user = Session["user"] as Uporabniki;
#Html.Raw(user.id)
#Html.ValidationMessageFor(model => model.id_avtorja, "", new { #class = "text-danger" })
</div>
</div>
And how can I fix this?
Try rewrite to
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,naslov,vsebina,datum,id_avtorja")] Vprasanja vprasanja)
{
if (ModelState.IsValid)
{
vprasanja.id = (Session["user"] as Uporabniki).id;
db.Vprasanja.Add(vprasanja);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.id_avtorja = new SelectList(db.Uporabniki, "id", "uporabniskoIme", vprasanja.id_avtorja);
return View(vprasanja);
}
Main idea - assign your id on post.
Please post some info on the error. But my guess is you aren't creating any input HTML element so there's nothing posting. You need something like <input type="hidden" id="id_avtorja" value="#user.id"> in the form.
Also, I'd advise against using data out of a session variable. That's older technology and very un-MVC in philosophy.

double and decimal not working

I have to set a price either a double or decimal.
I get this error and I have tried all I know.
Model:
public double price { get; set; }
Controller create:
public ActionResult Create([Bind(Include = "ID,farmID,productID,price, URL")] ProductFarm productFarm) {
if (ModelState.IsValid) {
db.farmProducts.Add(productFarm);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.farmID = new SelectList(db.farms.OrderBy(x => x.farmName), "farmID", "farmName", productFarm.farmID);
ViewBag.productID = new SelectList(db.products.OrderBy(x => x.productName), "productID", "productName", productFarm.productID);
return View(productFarm);
}
Edit - added view code:
<div class="form-group">
#Html.LabelFor(model => model.price, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.price, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.price, "", new { #class = "text-danger" })
</div>
</div>
This error is basically a locale issue. You have to set which locale you want to target to. By default the MVC scaffolding will default to your PC's culture.
You can set culture in your web.config
<system.web>
<globalization culture ="en-US" />
</system.web>
Since the issue is from the jquery validate plugin you can install jquery.validate.globalize
Install-Package jQuery.Validation.Globalize
Show's you how to use it https://johnnyreilly.github.io/jQuery.Validation.Unobtrusive.Native/AdvancedDemo/Globalize.html
Or,
You can extend your jquery.validate plugin yourself http://www.cedricascoop.be/blog/2011/10/22/mvc-3-problems-validating-a-decimal/
number: function (value, element) {
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value); //dot separated
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:\.\d{3})+)(?:,\d+)?$/.test(value); //comma separated
}
above code is from jquery.validate.js around line 1050 you can see the validation.Don't be confused I added the second return. I switched the return statement around. One return validates for dot another for comma.

Insert date from view to controller

I am filling a form in "view" and sending it back to "controller" class.
In my database i want to add the date and time of user creation.
code in my view
<div class="form-group">
#Html.LabelFor(model => model.Ticket_CreationDate, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Ticket_CreationDate)
#Html.ValidationMessageFor(model => model.Ticket_CreationDate)
</div>
</div>
code in controller class
public ActionResult Create([Bind(Include = "Ticket_SenderEmail,Ticket_Sujet,Ticket_Body,Ticket_CreationDate,Ticket_ClientID,Ticket_Priority,Ticket_UserID,Ticket_Status_ID")] Ticket ticket)
{
if (ModelState.IsValid)
{
db.Ticket.Add(ticket);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Ticket_ClientID = new SelectList(db.Client, "Client_Id", "Client_Nom", ticket.Ticket_ClientID);
ViewBag.Ticket_Priority = new SelectList(db.Priority, "Priority_Id", "Priority_Name", ticket.Ticket_Priority);
ViewBag.Ticket_Status_ID = new SelectList(db.Ticket_Status, "Ticket_Status_Id", "Ticket_Status_Nom", ticket.Ticket_Status_ID);
ViewBag.Ticket_UserID = new SelectList(db.User, "User_Id", "User_Nom", ticket.Ticket_UserID);
return View(ticket);
}
My question is how i can send current datetime from view to controller ?
You won't send the date from the view, you will handle that in the controller...
yourdataentity newentity = new yourdataentity();
//the rest of your implementation may be a bit different, but this is one way to set the date for a new record...
newentity.createDate = DateTime.Now;
yourentitys.Add(newentity);
if (ModelState.IsValid)
{
db.yourentitys.Add(newentity);
db.SaveChanges();
}

Categories