Accessing ViewBag data from Controller - c#

I'm still learning MVC and I don't know if it's possible or not, In my application, I'm doing an excel upload task.
So here Excel uploaded and I'm adding to those excel data to view bag and shows in the view.
for (int i = 2; i <= range.Rows.Count; i++)
{
try
{
M_Employee emp = new M_Employee();
string comName = ((Microsoft.Office.Interop.Excel.Range)range.Cells[i, 1]).Text;
var tempCompanyId = (from c in db.CreateCompany where c.CompanyName == comName select c.Id).First();
emp.CompanyId = tempCompanyId;
emp.EmpNo = int.Parse(((Microsoft.Office.Interop.Excel.Range)range.Cells[i, 2]).Text);
emp.EmpName = ((Microsoft.Office.Interop.Excel.Range)range.Cells[i, 3]).Text;
string dep = ((Microsoft.Office.Interop.Excel.Range)range.Cells[i, 4]).Text;
var tempDepId = (from d in db.CreateDepartment where d.Department == dep select d.Id).First();
emp.DepId = tempDepId;
string dessig = ((Microsoft.Office.Interop.Excel.Range)range.Cells[i, 5]).Text;
var tempDessId = (from h in db.CreateDesignation where h.Designation == dessig select h.Id).First();
emp.DesignId = tempDessId;
employees.Add(emp);
}
catch (Exception ex)
{
ViewBag.Error = "Error in " + i + " record";
return View("Import");
}
}
if (employees !=null)
{
ViewBag.EmpList = employees;
return View("Import");
}
In the view, It shows the excel imported data the user.
So to upload these data to the database table, I have created a button with mapping the upload action result in the view
<input type="button" value="Upload" class="btn btn-success" onclick="location.href='#Url.Action("UploadEmployees", "M_Employee")'" />
In that Action I tried to call those ViewBag.EmpList to get the same data and pass to the table.
foreach (var item in ViewBag.EmpList)
{
int ComId = item.CompanyId;
int EmpNo = item.EmpNo;
string EmpName = item.EmpName;
int DepId = item.DepId;
int DesId = item.DesignId;
}
But there I'm getting an error viewbag value is null. So is there any other way to do this?
Thanks
Editing--
This is my view
#{
ViewBag.Title = "Import";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div>
#using (Html.BeginForm("Import", "M_Employee", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="card card-primary">
<div class="card-header">
<h1 class="card-title"><b>Upload Employees from Excel</b></h1>
</div>
<!-- /.card-header -->
<div>
<br />
#Html.Raw(ViewBag.Error)
<h4><span>Select Excel File</span></h4>
<input type="file" name="excelFile" class="btn btn-warning" />
<br />
</div>
<div class="row">
<div class="col-md-1">
<br />
<br />
<input type="submit" value="Import" class="btn btn-info" />
</div>
<div class="col-md-1">
<br />
<br />
<input type="button" value="Upload" class="btn btn-success" onclick="location.href='#Url.Action("UploadEmployees", "M_Employee")'" />
</div>
</div>
<div class="card-body p-0">
<table class="table table-striped">
<tr>
<th>Company</th>
<th>EmpId</th>
<th>EmpName</th>
<th>Department</th>
<th>Dessignation</th>
</tr>
#if (ViewBag.EmpList != null)
{
foreach (var item in ViewBag.EmpList)
{
<tr>
<td>
#item.CompanyId
</td>
<td>
#item.EmpNo
</td>
<td>
#item.EmpName
</td>
<td>
#item.DepId
</td>
<td>
#item.DesignId
</td>
</tr>
}
}
</table>
</div>
<!-- /.card-body -->
</div>
}
</div>
This is the view model I have created.
[NotMapped]
public class EmpExcelUploadViewModel
{
public int CompanyId { get; set; }
public int EmpNo { get; set; }
public string EmpName { get; set; }
public int EmpDep { get; set; }
public int EmpDes { get; set; }
}

You can use ViewBag or ViewData for transferring data only one-way from Controller to View.
You should resend data from client's browser to your back-end with form.
Or if user can not edit data you can use any of:
Save file on the first step, then use the identifier of uploaded earlier file, re-read it for saving
Parse file and save parsed data into storage as draft on the first step, then just remove draft mark from data on the second step

You can not use ViewBag to move data from a client computer to a server. You can use Session or TempData (if the data is small) to keep data in the server, but I don' t recommend to use it since it affects app scalability.
You have 2 ways
Repeat downloading in your upload action and save data to a database
or you have to fix the action, using model instead of viewbag
....
if (employees !=null)
{
return View("Import", employees);
}
else ... you error code
in Import view add form and replace an ancor by submit button
#model List<Employee>
#using (Html.BeginForm("UploadEmployees", "M_Employee", FormMethod.Post))
{
.... emloyee input controls
<button type="submit" value="Upload" class="btn btn-success"> </>
}
in UploadEmployees action you can get data from an input parameter and save to database.

Related

C# MVC Adding a record to a table with form and parameters

I am having an issue with my add to a database record.
What I am attempting to do is add a record with parameters sent from a form. I am grabbing an item from a table giving a quantity and them adding it to an orders table.
I am adding it to the OrderDetails table so I need the orderId. Which I grab from the Url.
Below is my code, It is not working. It could probably be simplified but I am not sure where to modify this.
Form on Page:
#{ var OrderId = Request.Url.Segments[3];}
<td>
<form method="GET" action="~/OrderManager/AddToOrder/#OrderId" _lpchecked="1">
<div class="row ">
<div class="input-group mb-3 col-xs-6 col-md-6">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Qty</span>
</div>
<input style="max-width:75px;" aria-label="Qty" aria-describedby="inputGroup-sizing-default" type="number" class="form-control" id="quantity" min="1" name="quantity">
</div>
<div class="col-xs-6 col-md-6">
<button class="btn btn-primary btn-sm" type="submit" id="submit" onchange="usercheck">Add To Order</button>
</div>
</div>
</form>
</td>
Here is my Controller actions:
public ActionResult Add(int id)
{
try
{
GeneralEntities db = new GeneralEntities();
var result = db.Extras.ToList().OrderByDescending(x => x.CreatedDate);
return View(result);
}
catch (Exception ex)
{
throw ex;
}
}
public ActionResult AddToOrder(int OrderId, string id, int quantity)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
try
{
GeneralEntities ExtrasDb = new GeneralEntities();
var addedExtra = ExtrasDb.Extras
.Single(ext => ext.ExtrasName == id);
var extra = new OrderDetail
{
OrderId = OrderId,
Extras = addedExtra.BillingExtrasId,
Quantity = quantity,
CreatedDate = DateTime.Now
};
ExtrasDb.OrderDetails.Add(extra);
sb.Append("Sumitted");
return Content(sb.ToString());
}
catch (Exception ex)
{
sb.Append("Error :" + ex.Message);
}
return Content(sb.ToString());
}
Thanks for your help!
Revised Code Update:
I changed some things. I seem to have what i need now and it says it submits but it does not save it to the table..
Revised form:
#{ var OrderId = Request.Url.Segments[3];}
<td>
<form method="POST" action="~/OrdersManager/Add/" _lpchecked="1">
<div class="row ">
<div class="input-group mb-3 col-xs-6 col-md-6">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Qty</span>
</div>
<input data-val="true" id="OrderId" name="OrderId" type="hidden" value="#OrderId" />
<input data-val="true" id="id" name="id" type="hidden" value="#item.BillingExtrasId" />
<input style="max-width:75px;" aria-label="Qty" aria-describedby="inputGroup-sizing-default" type="number" class="form-control" id="quantity" min="1" name="quantity">
</div>
<div class="col-xs-6 col-md-6">
<button class="btn btn-primary btn-sm" type="submit" id="submit" onchange="usercheck">Add To Order</button>
</div>
</div>
</form>
</td>
Revised Controller Code:
[HttpPost]
public ActionResult Add(int OrderId, Guid id, int quantity)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
try
{
GeneralEntities ExtrasDb = new GeneralEntities();
// Retrieve the album from the database
var addedExtra = ExtrasDb.Extras
.Single(ext => ext.BillingExtrasId == id);
var extra = new OrderDetail
{
OrderId = OrderId,
Extras = addedExtra.BillingExtrasId,
UnitPrice = addedExtra.Price,
Quantity = quantity,
CreatedDate = DateTime.Now
};
ExtrasDb.OrderDetails.Add(extra);
sb.Append("Sumitted");
return Content(sb.ToString());
}
catch (Exception ex)
{
sb.Append("Error :" + ex.Message);
}
return Content(sb.ToString());
}
Add
ExtrasDb.SaveChanges();
right before the first return statement inside of the try block.
(You found the mistake and mentioned it in your comment. I'm just confirming and turning it into an answer.)

Losing Session variable on updating values

I am using asp .net 4 using mvc framework.
When I update a value, my session gets lost.
I cannot seem to figure out why. I am new to ASP.
Lets say I have Session["sValue"] (= "test").
After executing the Index(), that variable is lost
Note: It only happens if !string.IsNullOrEmpty(CallPrice) is True, so CallPrice has a value.
The Controller:
// GET: Settings
[Route("Settings")]
public ActionResult Index()
{
SettingsModel pageSettings = new SettingsModel();
string CallPrice = Request.QueryString[pageSettings.Query_CallPrice];
ViewBag.Result = "";
try
{
if (!string.IsNullOrEmpty(CallPrice))
{
pageSettings.Price = Convert.ToInt32(CallPrice);
SettingsManager.call_price = CallPrice;
ViewBag.Result = "Update Succesful.";
}
}
catch (FormatException)
{
if (!string.IsNullOrEmpty(CallPrice))
pageSettings.Price = Convert.ToInt32(SettingsManager.call_price);
ViewBag.Result = "Error Occured (Incorrect format provided)";
}
return View(pageSettings);
}
The Page:
#model Actacom3CX.Models.SettingsModel
#{
ViewBag.Title = "3CX Settings";
}
<head>
#{
if (HttpContext.Current.Session[Actacom3CX.Classes.Statics.SESSION_USERNAME] == null)
{
Response.Redirect("~/UserLogon", false);
}else
{
HttpContext.Current.Session[Actacom3CX.Classes.Statics.SESSION_USERNAME] = HttpContext.Current.Session[Actacom3CX.Classes.Statics.SESSION_USERNAME];
}
}
</head>
<div class="jumbotron" style="margin-top: 0em; padding-bottom:0.4em;padding-top:0.5em;">
<div align="center">
<h2 style="margin:0em;padding:0em;">
#ViewBag.Title
</h2>
<h3 align="center">
#{
Output.Write(ViewBag.Result);
}
</h3>
</div>
</div>
<div class="row">
<form id="settingsForm" method="get" action="~/Settings">
<input class="btn btn-default" type="text" name=#{Output.Write(Model.Query_CallPrice);} value=#{Output.Write(Model.Price);} /><b style="padding-left: 1em;">Cent per minuut</b>
<br /><br />
<input class="btn btn-default" type="submit" value="Update ยป" />
</form>
</div>
UPDATE
The Session getting lost is happening in the following piece of code, in the SettingsManager:
public static void SetSetting(string key, string value)
{
Configuration configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
configuration.AppSettings.Settings[key].Value = value;
configuration.Save();
ConfigurationManager.RefreshSection("appSettings");
}
Thanks to Hans Kesting:
It seems you are updating the web.config file. When that is updated, the webapp restarts, which causes all Sessions to get lost (even from other users!).

Binding my view model to a view causes all check boxes to be checked

I'm having an issue where my all the check boxes rendered in the view are coming out checked. I put a breakpoint at the line where my view model is constructed and through the debugger, I can see that some values are set to "true" and others are set to "false". So the problem, I'm assuming, has got to be in the view itself.
Here is my model:
public class UserModulesAdministrationViewModel
{
public bool AccessGranted { get; set; }
public int ModuleId { get; set; }
public string ModuleName { get; set; }
public string ModuleDescription { get; set; }
}
Here is my controller that is rendering the list:
public ActionResult UserModules(int id)
{
// Database stuff here
var model = modules.Select(module => new Infrastructure.ViewModels.UserModulesAdministrationViewModel
{
ModuleId = module.AccessModuleId,
AccessGranted = userModules.Any(z => z.AccessModuleId == module.AccessModuleId),
ModuleName = module.ModuleName,
ModuleDescription = module.ModuleDescription
}).ToList();
return View(model);
}
And finally here is my relevant view code:
#model IEnumerable<UserModulesAdministrationViewModel>
#foreach (UserModulesAdministrationViewModel m in Model)
{
<div class="col-md-4" style="margin-top: 15px;">
<div class="moduleBlockLong" style="position: relative">
<div class="moduleHeader">#m.ModuleName</div>
<div class="moduleText">#m.ModuleDescription</div>
<div class="checkbox" style="position: absolute; bottom: 0; right: 80px">
<label>
#{
var m1 = m;
}
#Html.CheckBoxFor(z => m1.AccessGranted )
<input type="checkbox" value="" checked="checked"/> Allow Access
</label>
</div>
</div>
</div>
}
The problem seems to me like you have hardcoded the input after the CheckBoxFor HtmlHelper.
#Html.CheckBoxFor(z => m1.AccessGranted )
<input type="checkbox" value="" checked="checked"/>
Remove:
<input type="checkbox" value="" checked="checked"/>
It's also worth noting that as you are using a foreach loop rather than a for loop that you will not be able to post the selected values back to the server.
You will need to index your loop as follows:
#for (var i = 0; i < Model.Count; i++)
{
// code
#Html.CheckBoxFor(z => Model[i].AccessGranted)
// rest of code
}
Or you will not be able to read any user input on the server.
In your view, remove
<input type="checkbox" value="" checked="checked"/> Allow Access
Because of checked="checked", this will always print out a checked checkbox.
I think that happens because you left <input type="checkbox" value="" checked="checked"/>
Remove it and it will works.
Also there exist another problem about foreach loop.
ASP.NET MVC 4 - for loop posts model collection properties but foreach does not
Solution:
#for(var i = 0; i<Model.Count; i++)
{
<div class="col-md-4" style="margin-top: 15px;">
<div class="moduleBlockLong" style="position: relative">
<div class="moduleHeader">#Model[i].ModuleName</div>
<div class="moduleText">#Model[i].ModuleDescription</div>
<div class="checkbox" style="position: absolute; bottom: 0; right: 80px">
<label>
#Html.CheckBoxFor(z => Model[i].AccessGranted) Allow Access
</label>
</div>
</div>
</div>
}
Try this code...
Instead of : <input type="checkbox" value="" checked="checked"/> Allow Access
Try:
<input type="checkbox" value="" checked="#m.AccessGranted "/> Allow Access
In addition, don't use m1 parameter..

Have to click submit twice to get it to write to the database

I am having an issue with the following code, it IS working, but the first time you click on the "Update" button (which is the forms 'Submit'), the value in price will revert back to the current value. If you edit the value again and click on "Update" it will write the value to the database (?????)
#section Scripts {
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
}
#{
//Get the layout page and assign the page title
Layout = "~/SiteManager/_ManagerLayout.cshtml";
Page.Title = "Modify Listing";
//Get the productId from the query string
var ProductId = Request.QueryString["ProductId"];
//Setup validation
Validation.RequireField("productTitle", "A product title is required.");
Validation.RequireField("productDescription", "A product description is required.");
Validation.RequireField("productPrice", "A product price is required.");
//Open the database
var db = Database.Open("sg_cart");
//Build the query to obtain the recordset
var qrySelectedProduct = "SELECT * FROM Products WHERE Id = #0";
//Pass the required parameters and get the currently selectd product
var selectedProduct = db.QuerySingle(qrySelectedProduct, ProductId);
if(IsPost)
{
// Validate that request came from the correct form
AntiForgery.Validate();
if(ModelState.IsValid)
{
var productTitle = Request.Form["productTitle"];
var productDescription = Request.Form["productDescription"];
var productPrice = Request.Form["productPrice"];
var qryUpdateProduct = "UPDATE Products SET Name = #0, Description = #1, Price = #2 WHERE Id = #3";
db.Execute(qryUpdateProduct, productTitle, productDescription, productPrice, ProductId);
}
}
}
<h1>#Page.Title for #selectedProduct.Name</h1>
<br />
<div class="container">
<form class="pure-form pure-form-stacked alert alert-success alert-block" action="" method="post">
#*Generate html key to validate this form *#
#AntiForgery.GetHtml()`
#* If one or more validation errors exist, show an error *#
#Html.ValidationSummary("Update product was unsuccessful. Please correct the errors and try again.")
<fieldset>
<legend hidden>Edit #selectedProduct.Name</legend>
<table>
<tr>
<td class="content-cell-left">
<div class="pure-control-group">
<label for="productTitle">Product Title</label>
<input class="form-control" type="text" name="productTitle" value="#selectedProduct.Name" />
#Html.ValidationMessage("productTitle")
</div>
<div class="pure control-group">
<label for="productDescription">Product Description</label>
<textarea rows="5" cols="100" name="productDescription">#selectedProduct.Description</textarea>
#Html.ValidationMessage("productDescription")
</div>
<div class="pure-control-group">
<label for="productPrice">Product Price</label>
<input class="form-control" type="text" name="productPrice" value="#String.Format("{0:0.00}", selectedProduct.Price)" />
#Html.ValidationMessage("productPrice")
</div>
<div>
<input type="submit" value="Update" causesValidation="true"/>
<input type="button" value="Change Image" onclick='window.location.href="EditProductPicture?ProductId=#selectedProduct.Id"' />
<input type="button" value="Cancel" onclick='window.location.href="CartManager";' />
</div>
</td>
<td class="content-cell-right">
<img src="/Images/Products/#selectedProduct.ImageName" alt="#selectedProduct.ImageName" />
</td>
</tr>
</table>
</fieldset>
</form>
</div>

post values through mvc repeater

Form-
#using IEnumerable<Myapplication.Models.CardModel>
#foreach(var item in Model)
{
<form method="post" action="/Upload/EditCard/?cardID=#item.cardID" enctype="multipart/form-data">
<h3>
Change Title-
</h3>
<div class="display-field">
#Html.HiddenFor(m => item.cardTitle)
#Html.TextBoxFor(cardTitle => item.cardTitle)
</div>
<img src="#item.cardFilePath" />
<input type="submit">
</form>
}
Method-
[HttpPost]
public void EditCard(CardModel card, HttpPostedFileBase file) {}
Where in form I am sending values through this form, and cardID is sent in form's url parameter.
For other value like cardTitle is coming null in EditCard Method.
How do I get this value using repeater?
However when data is not IEnumerable type , then I was able to send value through form directly as-
#using Myapplication.Models.CardModel
<form method="post" action="/Upload/EditCard/?cardID=#Model.cardID" enctype="multipart/form-data">
<h3>
Change Title-
</h3>
<div class="display-field">
#Html.HiddenFor(m => m.cardTitle)
#Html.TextBoxFor(m=> m.cardTitle)
</div>
<img src="#Model.cardFilePath" />
<input type="submit">
</form>
}
but In case of repeater values don't come at method. As soon as i change values of title, or anything else, The values comes old one.
From the pictures-
And server code-
As you can see from 1st picture that, Form is editable for one record from the list. As soon as I change something in i.e. Title to --> 2nd Upload to 2nd Uploadsssss
Then this values is null at server side. I mean this form don't send values of it.
Note-
However I can send values in URL through parameters. But if I do change in something like Title or aboutCard model value. The form keeps sending only those values which has come by default while the form was rendered.
Rather than using foreach use for loop. To apply indexing you would need to convert Model to List
#{ var list=Model.ToList();)
#for(var i = 1;i <= list.Count();i++)
{
<form method="post" action="/Upload/EditCard/?cardID=#list[i].cardID" enctype="multipart/form-data">
<h3>
Change Title-
</h3>
<div class="display-field">
#Html.HiddenFor(m => list[i].cardTitle)
#Html.TextBoxFor(m=> list[i].cardTitle)
</div>
<img src="#list[i].cardFilePath" />
<input type="submit">
</form>
}
Update..
I have tested same but doesnt work. I have created workaround check if that helps.
In controller get title using cardId
var title=Request["[" + (model.cardTitle)+ "].cardTitle"];
Same thing can be done for other properties for model.
Note that i have changed for loop, index now starts with 1
You need to change IEnumerable to IList, and then you'll be able to bind Model correctly by index.
See this working exaple.
View Page
#model IList<MvcApplication3.Models.CardModel>
#using (Html.BeginForm("EditCard", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
for (int i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(m => Model[i].CardID)
#Html.TextBoxFor(cardTitle => Model[i].CardTitle)
<img src="#Model[i].CardFilePath" />
<input type="file" name="files">
}
<br>
<input type="submit" value="Upload File to Server">
}
Controller
[HttpPost]
public void EditCard(IList<CardModel> cm, IEnumerable<HttpPostedFileBase> files)
{
string strfile = string.Empty;
string cardTitle = string.Empty;
if (files != null)
{
foreach (var file in files) //loop through to get posted file
{
strfile = file.FileName;
}
}
if (cm != null)
{
foreach (var item in cm) //loop through to get CardModel fields
{
cardTitle = item.CardTitle;
}
}
}
I dont know if this helps in solving your problem. I tried this and it works really well.
The Model
public class CardFileModel
{
public int CardId { get; set; }
public string Name { get; set; }
public HttpPostedFileBase File { get; set; }
}
Index Action
public ActionResult Index()
{
List<CardFileModel> cards = new List<CardFileModel>
{
new CardFileModel{ CardId =1 , Name="Card1" },
new CardFileModel{ CardId =2 , Name="Card2" }
};
return View(cards);
}
Home View
#model List<MVC3Stack.Models.CardFileModel>
#{
ViewBag.Title = "Home Page";
}
<h2>#ViewBag.Message</h2>
#for (int i = 0; i < Model.Count; i++)
{
using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div>
<div>
#Html.TextBoxFor(x => x[i].CardId)
</div>
<div>
#Html.TextBoxFor(x => x[i].Name)
</div>
#Html.TextBoxFor(x => x[i].File, new { type = "file" })
<input type="submit" value="submit" id="submit" />
</div>
}
}
index post action
[HttpPost]
public ActionResult Index(List<CardFileModel> card)
{
//Access your card file from the list.
var name = card.FirstOrDefault().Name;
return View();
}
And here are the results in the quick watch
Hope this helps.

Categories