How to prevent inserting null value in Image field when the record is modified and saved using MVC4? - asp.net-mvc-4

I am storing image in Database successfully.
I want to display this image in Edit form to modify and save changes. But I'm just able to display image in Edit form my code for modifying and save images in data base is not working it's inserting null values in Image field when the record is modified and saved or if I modify all other fields in Edit view excepting image field.
Would someone please tell me what mistake I'm doing? Here is my controller action:
public ActionResult Edit(student st) {
if (ModelState.IsValid) {
var imgFile = Request.Files["imgFile"];
if (imgFile != null && imgFile.ContentLength > 0) {
var fileName = Path.GetFileName(imgFile.FileName);
var path = Path.Combine(Server.MapPath("~/Content/stImgs"), fileName);
imgFile.SaveAs(path);
st.Img = fileName;
}
}
try {
db.Entry(st).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("student");
} catch {
return View(st);
}
}
Here is view:
<img src="/Content/Imgs/#Model.Img">
</div>
<label for="file">Image:</label>
<input type="file" name="file" id="file" />
#Html.ValidationMessageFor(item => item.file)

try adding a HttpPostedFileBase parameter in your action method:
public ActionResult Edit(student st, HttpPostedFileBase file)
{
if (ModelState.IsValid) {
if (file != null && file.ContentLength > 0) {
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/Content/stImgs"), fileName);
file.SaveAs(path);
st.Img = fileName;
}
}
try {
db.Entry(st).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("student");
} catch {
return View(st);
}
}
Make sure the parameter's name is the same as the file's name in the form!
Also, make sure your Form is set to enctype multipart/form-data:
Html.BeginForm(
action, controller, FormMethod.Post, new { enctype="multipart/form-data"})

Microsoft change how update action method work.
please read section "Update the Edit HttpPost Method " from the following link.
These changes implement a security best practice to prevent overposting, The scaffolder generated a Bind attribute and added the entity created by the model binder to the entity set with a Modified flag. That code is no longer recommended because the Bind attribute clears out any pre-existing data in fields not listed in the Include parameter. In the future, the MVC controller scaffolder will be updated so that it doesn't generate Bind attributes for Edit methods.
The new code reads the existing entity and calls TryUpdateModel to update fields from user input in the posted form data. The Entity Framework's automatic change tracking sets the Modified flag on the entity. When the SaveChanges method is called, the Modified flag causes the Entity Framework to create SQL statements to update the database row. Concurrency conflicts are ignored, and all columns of the database row are updated, including those that the user didn't change. (A later tutorial shows how to handle concurrency conflicts, and if you only want individual fields to be updated in the database, you can set the entity to Unchanged and set individual fields to Modified.)

Related

Why is my record being deleted from the db when I attempt to update the record from entity framework MVC?

When I attempt to update a record from entity framework the record is being deleted from the table. There are no errors thrown so it really has me baffled what is happening.
I am fairly new to entity framework and asp.net. I've been learning it for about a month now.
I can update the record without any issues from SQL Server but not from vs. Here is the code to update the db:
// GET: /Scorecard/Edit/5
public ActionResult Edit(int id, string EmployeeName)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
CRS_Monthly crs_monthly = GetAgentById(id);
crs_monthly.EmployeeName = EmployeeName;
if (crs_monthly == null)
{
return HttpNotFound();
}
return View(crs_monthly);
}
// POST: /Scorecard/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="REC_ID,Cur_Plan,Plan_Update,Comments,Areas_Improve,Strengths,UPDATED_BY,UPDATED_TIME,Agent_Recognition")] CRS_Monthly crs_monthly)
{
if (ModelState.IsValid)
{
crs_monthly.UPDATED_TIME = DateTime.Now;
crs_monthly.UPDATED_BY = Request.LogonUserIdentity.Name.Split('\\')[1];
db.Entry(crs_monthly).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(crs_monthly);
}
When I run the debugger crs_monthly is valid and looks fine until db.SaveChanges(). Any help is greatly appreciated!
You should never save an instance of your entity created from a post, especially when you're utilizing Bind to restrict which properties are bound from the post data. Instead, always pull the entity fresh from the database and map the posted values on to it. This ensures that no data is lost.
Using Bind is a horrible practice, anyways. The chief problem with it is that all your properties are listed as string values, and you're introducing maintenance concerns. If remove one of these properties or change the name, the Bind list is not automatically updated. You must remember to change every single instance. Worse, if you add properties, you have to remember to go back and include them in this list or else your data just gets silently dropped with no notice.
If you need to only work with a subset of properties on your entity, create a view model containing just those properties. Then, again, map the posted values from your view model onto an instance of your entity pulled fresh from the database.

Data is not inserting to the database

I am new to asp .net MVC 4.
I have one text box and the text box value I am fetching from one table.But while clicking on submit button this value I want to insert into different table , which is not inserting and showing error.It is taking value as null.
coding
View
#Html.TextBox("empname", (string)ViewBag.empname, new { #readonly = "readonly" })
controller
[HttpGet]
public ActionResult Facilities()
{
mstEmpDetail emp = new mstEmpDetail();
emp = db.mstEmpDetails.Single(x => x.intEmpId == 10001);
ViewBag.empname = emp.txtEmpFirstName;
return View();
}
[HttpPost]
public ActionResult Facilities(TrnBusinessCardDetail bc)
{
var empname1 = ViewBag.empname;
bc.txtfirstName = empname1;
db.TrnBusinessCardDetails.Add(bc);
db.SaveChanges();
return RedirectToAction("Facilities");
}
While I was working with normal text box it was inserting properly,but when I have retrieve
fro DB then i am getting this problem ?
How to solve this problem ?
Viewbag is a one way street - you can use it to pass information to the view, but you cannot use it to get the information from the view. The statement ViewBag.empname in your POST method has a value of null in your code.
As suggested by #dotnetom, ViewBag is a one way street. MVC is stateless so a POST request is not a "Round Trip" from previous get request. Thus your ViewBag can not hold its state.
MVC can determine (and construct) your action parameters from Form Parameters. In your case you have added a textbox with name "empname". So you should get this value as parameter in your POST request.
[HttpPost]
public ActionResult Facilities(TrnBusinessCardDetail bc, string empname)
{
bc.txtfirstName = empname;
db.TrnBusinessCardDetails.Add(bc);
db.SaveChanges();
return RedirectToAction("Facilities");
}
This would be simplest of solution given your problem. More appropriate would be binding your textbox directly with you model property. This way you will not have to worry about retrieving and assigning property value to model in your controller.
I think the problem is when you are using var empname1 = ViewBag.empname; in post controller because ViewBag.empname lost its value at that time.

Variable number of HttpPostedFileBase objects

Currently, I have this:
public ActionResult Add(FormCollection form, HttpPostedFileBase fr, HttpPostedFileBase en, HttpPostedFileBase es)
{
Upload(fr, "fr");
Upload(en, "en");
Upload(es, "es");
...
}
This works for what we're doing currently, but just learned of a new requirement where the system needs the ability to add other languages. This is the only part where I have an issue.
I tried:
public ActionResult Add(FormCollection form, HttpPostedFileBase[] fr)
{
foreach(var file in fr)
{
Upload(file, "I'mStuck");
}
...
}
as a test, but it will only have 1 element and it is the one where id/name = fr. Makes sense, but not particularly helpful for what I need.
I could do:
for (string file in Request.Files)
{
...
}
which would handle the upload component fine, but the issue is that unless I can force them to standardize against a whatever_langabbreviation.extension file format, which I can't, I'm not going to be able to know what the language abbreviation is.
How can I obtain the id/name fields for the input type=file objects within the controller?
I was actually incorrect. The string returned is actually the id or name (think name, but considering I typically pair id/name, it works).
For the controller that renders the view initially, I did:
List<Languages> langs = db.Languages.ToList();
viewmodel.Languages = langs;
return View(viewmodel);
In the view itself:
foreach(Language lang in Model.Languages)
{
// Label
<input type="file" id="#lang.Abbreviation" name="#lang.Abbreviation" />
}
And in the post event:
foreach(string file in Request.Files)
{
HttpPostedFileBase fb = Request.Files[file];
Upload(fb, file);
}
And it handles as it is supposed to (Upload being a function that just adds a new item to another table.

How to apply CSS to Html.ValidationSummary at runtime

I am building my first MVC4 website and I would like to show success message when page successfully submitted. I have achieved by using ModelState.AddModelError(("", "Data successfully saved."); but it is showing in the red color. I want to apply different css at runtime based on some conditions.
Thanks.
I recommend using TempData instead of changing validationsummary and #von described it very well. Use bootstrap. You could do something like this:
Controller
[HttpPost]
public ActionResult ManageUsers(UserViewModel model)
{
if (ModelState.IsValid)
{
User obj = new User();
obj.UserName = model.Email;
obj.FirstName = model.FirstName;
obj.LastName = model.LastName;
obj.Email = model.Email;
obj.Phone = model.Phone;
obj.Notes = model.Notes;
obj.Authorized = model.Authorized;
UserRepository.SaveUser(obj);
TempData["Success"] = "Yes";
}
return View(model);
}
View
#Html.ValidationSummary()
#if (TempData["Success"] != null)
{
<div class="alert alert-success">
×
<strong>Success!</strong> The User account was created.
</div>
}
Normally when the result of an action method is successful a redirect happens, maybe that's what you want, especially if your result is not a json result. But if you are returning the same view after your post then you are doing it incorrectly. If the ModelState is valid on a post, that is if the validation passed (e.g. required fields are supplied), and you add an error message by doing ModelState.AddModelError(("", "Data successfully saved.") then you are making the ModelState go into an invalid state. That is the reason why you have the red color.
Now assuming you really want to return the same view then I suppose you have something like:
[HttpPost]
public ActionResult YourActionMethod(YourModel model)
{
// some code goes here
ModelState.AddModelError(("", "Data successfully saved.")
return View(", model);
}
What you should have instead is something like this:
[HttpPost]
public ActionResult YourActionMethod(YourModel model)
{
// some code goes here
ViewBag.SuccessMessage = "Data successfully saved.";
return View(", model);
}
Then on your view something like:
#Html.ValidationSummary(true)
if (!string.IsNullOrWhiteSpace(ViewBag.SuccessMessage)) {
<div class="success-summary">
<p>#ViewBag.SuccessMessage</p>
</div>
}
Note that you don't need an additional # before the if, that code assumes it's inside a form tag, using #using. And then for the css:
.success-summary {
color: #3366FF;
}
You can actually use either ViewData or ViewBag. To know more about the difference of the two you can visit this SO page.
UPDATE:
[HttpPost]
public ActionResult YourActionMethod(YourModel model)
{
//
If (ModelState.IsValid) {
#ViewBag.IsModelValid = true;
ModelState.AddModelError("", "Data successfully saved.");
return View(model);
}
ViewBag.SuccessMessage = "Data successfully saved.";
return View(", model);
}
Your view:
#Html.ValidationSummary(false, "", new { #class= (ViewBag.IsModelValid!=null && ViewBag.IsModelValid) ? "success-summary" : "" })
Von, I too appreciate your answer, but I agree with MaxPayne that you didn't quite provide an answer for the question, more of a work around IMO.
I too am looking at a way to style the ValidationSummary without the extra baggage of using the ViewBag.
I do agree that you shouldn't return to the same view after a post unless there are errors, but I do believe there are times when one might want to change the ValidationSummary style dynamically without having to use the ViewBag.
So far this is my only lead http://westcountrydeveloper.wordpress.com/2011/07/06/mvc-validation-part-4-styling-the-validation-controls/
I suppose you could use some JQuery to change the element's css attributes based on the Validation response.
var valid = $("#formID").validate().element("#ElementID");
//or
var valid = $('#formID').validate();
// Then use $(".ElementClass").css({}); to change the the style

Persisting MVC4 controller data thru multiple post backs

I have a MVC4 controller that calls its view multiple times, each time with a different set of ViewBags. The view renders the contents of a Form based on the absence or presence of those ViewBags via something like this:
#using (Html.BeginForm())
{
#if (ViewBag.Foo1 != null)
{
#Html.DropDownList("Bar1",....
}
#if (ViewBag.Foo2 != null)
{
#Html.DropDownList("Bar2",....
}
<input name="ActionButton" type="submit" value="Ok"" />
}
Each time the user clicks the submit button, the controller checks to see what is in the collection and makes a new set of ViewBags before calling the view again, sort of like this:
public ActionResult Create()
{
ViewBag.Foo1 = "blawblaw";
return View();
}
[HttpPost]
public ActionResult Create(FormCollection collection)
{
if (collection["Bar1"] != null)
{
string FirstPass = collection["Bar1"];
ViewBag.Foo2 = "blawblaw";
}
if (collection["Bar2"] != null)
{
string SecondPass = collection["Bar2"];
ViewBag.Foo3 = "blawblaw";
}
return View();
}
What I need to do now is somehow have each pass thu the controller 'remember' something about its previous passes. That is, in my example, the second pass thru the controller (the one where collection["Bar2"] is true), the value of FirstPass is null.
How can I do that?
In that case have a look at best practices for implementing a wizard in MVC. Some good suggestions here. Personally I would still consider using separate and distinct urls. Also, If you have db access in your solution you can still store temporary data before updating the main model. Think about what you want to happen if the user doesn't complete the whole journey the first time round...