AmbiguousMatchException in my VIew Page - asp.net

I am using ExcelReader to upload new sheet of containing data and get all old data from the DbSet.I am using Entity Framework POCO way to POST/GET data. I want to use same view for both POST/GET.I have two submit button one for saving current data and one for get all data from datacontext.Now my View Code is below I followed the steps included here!
I getting following error "System.Reflection.AmbiguousMatchException". I am guessing this because of HTNL.Beginfrom() creating it because of action result name.
Can any one help me how to resolve this issue if I need to use #Html.AntiForgeryToken();
#Html.ValidationSummary(); in my both the GET/POST call.
[MultipleButton(Name = "action", Argument = "Index")]
public ActionResult Index(HttpPostedFileBase uploadfile,dataViewModel dataView1)
{
// code for upload
}
[MultipleButton(Name = "action", Argument = "ExcelData")]
public ActionResult ExcelData()
{
ReadContext Context = new ReadContext();
return View(Context.dataext.ToList());
}
My View code:
#{
ViewBag.Title = "Read Excel File and Display in mvc5";
}
#model System.Data.DataTable
#using System.Data
<h2>Read Excel File and Display in mvc5</h2>
#using (Html.BeginForm("Index", "ReadExcel", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken();
#Html.ValidationSummary();
<label class="text-info">Upload Excel File</label>
<input type="file" class="form-control" name="uploadfile" id="uploadfile" />
<input type="submit" value="submit" class="btn btn-default" name="action:Index" />
<input type="submit" value="data" class="btn btn-default" name="action:ExcelData" />
if (Model != null)
{
<table class="table table-responsive table-bordered">
<thead>
<tr>
#foreach(DataColumn column in Model.Columns)
{
<th>#column.ColumnName</th>
}
</tr>
</thead>
<tbody>
#foreach(DataRow row in Model.Rows)
{
<tr>
#foreach(DataColumn col in Model.Columns)
{
<td>#row[col.ColumnName]</td>
}
</tr>
}
</tbody>
</table>
}
}

Related

How can pass the List of dynamically generated input fields form from the view to controller in asp.net MVC

I have dynamically generated input fields but I am unable to model binding and pass the list of values view to controller when I post the data without list then mapped.when I post the form then I am getting null object, there is model binding issue
[HttpPost]public ActionResult PatientVitals(List<PatientVitals> pvitalsDetails)
{
var enterBy = SessionHelper.GetLoggedInUserId();
var patientVitals = new PatientVitals();
var patient = (Session["Patient"] as Patient) ?? new Patient();
var response = new AjaxResponse
{
Code = 400,
MessageClass = "warning",
ResponseText = "Unable to process"
};
return response}
This is Razor view with name space
#model List<PKLI.Model.Emr.PvitalsDetails>
this is Post form
#using(Html.BeginForm("PatientVitals", "EMR", FormMethod.Post))
{
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Vitals/Measurement</th>
<th>Values</th>
<th>Unit</th>
<th>Time Taken</th>
</tr>
</thead>
<tbody>
#if (Model != null) {
int j = 0;
foreach (var item in Model)
{
<tr>
<td>
#item.Name
<input type="hidden" name="#Model[j].VitalsInformationId" id="vitalId+#j" value="#item.VitalsInformationId"/>
</td>
<td>
<input placeholder="#item.Name" type="text" name="#Model[j].Readings" id="Reading+#j"class="form-control" />
</td>
<td>
#item.Unit
<input type="hidden" name="#Model[j].Unit" id="Unit+#j"class="form-control" />
</td>
<td>
<input type="text" name="#Model[j].Time" id="Time+#j" class="form-control"placeholder="Enter Time" data-mask="99:99" />
</td>
</tr>
j++;
}
}
</tbody>
</table>
</div>
}
Try using viewbag object instead of var.
viewbag.id = 0;
viewbag.enterby= SessionHelper.GetLoggedInUserId();
var patientVitals = new PatientVitals();
viewbag.patient = (Session["Patient"] as Patient) ?? new Patient();
var response = new AjaxResponse
{
Code = 400,
MessageClass = "warning",
ResponseText = "Unable to process"
};

How to post table rows data to a controller w/o using ajax in asp.net core

What is want to achieve is I have a form to adds rows with data to a html table, it's like a temporary table and all the data from it will be added in just one submit button. How can I possibly do this?
This is my sample table structure, data from it must be added to db
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<table class="table">
<thead>
<tr>
<!--some other fields-->
<th>Amount</th>
<th>Check #</th>
<th>Check Date</th>
</tr>
</thead>
<tbody>
<tr>
<!--some other fields-->
<td>
<input asp-for="Amount" class="form-control" />
<span asp-validation-for="Amount" class="text-danger"></span>
</td>
<td>
<input asp-for="Check_No" class="form-control" />
<span asp-validation-for="Check_No" class="text-danger"></span>
</td>
<td>
<input asp-for="Check_Date" class="form-control" />
<span asp-validation-for="Check_Date" class="text-danger"></span>
</td>
</tr>
<!--row 2-->
<!--row 3-->
<!--row 4-->
<!--etc..-->
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
And here is my controller code so far, i don't know what do I need to change
// POST: Books/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 async Task<IActionResult> Create([Bind("Id,Year,Month,Document_Code,GLA_Code,Expense_Code,Function_Code,Document_Reference_No,Document_Date,Quantity,Amount,Check_No,Check_Date,Remarks,Encoder")] Book book)
{
if (ModelState.IsValid)
{
_context.Add(book);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(book);
}
What must needed to be change in my view and controller, and help will is appreciated.
Thank you.
There's a couple of issues here.
First, if you've got a table of "books" (plural), the input names need to be indexed. You then also need to accept something like List<Book> instead of Book as the param to your action method. It's a little hard to tell with just the code provided, but I'd imagine you're repeating these inputs, all with the same names for each row. If that's the case, only the values for the last item will be posted. Making it a list of items enables you to post them all.
Simplistically, that means your inputs need to have names like [0].Amount, which Razor will generate for you if you use a for loop and render the inputs like:
<input asp-for="#Model[i].Amount" class="form-control" />
If you're adding the additional rows (and contained inputs) via JavaScript, you'll need to ensure that you're generating these indexed names properly yourself. A JS templating library may help in this regard.
Second, do not use Bind. Just don't. It's awful, horrible, and kills both puppies and kittens. For more explanation see my post, Bind is Evil. Use a view model instead. As a general rule you should never post an entity class. Your entity classes serve the database and its concerns, which are almost always different from the concerns of the view. Additionally, you should never just blindly save something posted by a user. Even if you insist on using your entity class to bind to, you can improve the security and safety of your code exponentially by literally mapping the values from the posted version of the class over to a new instance you create. Then, you know exactly what is being persisted to the database (without the godforsaken Bind) and you also have the opportunity to sanitize input as necessary.
I was facing a similar problem, but using ASP.NET Core 3.1 and Razor Pages. I was looking for a way to add and remove rows from a table with JavaScript, and then post it. My problem was to post the table without Ajax. Based in the question and in the accepted answer, I could do that.
Here is Index.cshtml.cs:
public class IndexViewModel {
public string Name { get; set; }
public IList<ResourceViewModel> Resources { get; set; }
}
public class ResourceViewModel {
public string Key { get; set; }
public string Value { get; set; }
}
public class IndexModel: PageModel {
[BindProperty]
public IndexViewModel ViewModel {get; set; }
public void OnGet() {
// You can fill your ViewModel property here.
}
public void OnPost() {
// You can read your posted ViewModel property here.
}
}
Here is Index.cshtml:
#page
#model IndexModel
#{
ViewData["Title"] = "Index";
}
<form method="post">
<div class="form-group">
<label asp-for="ViewModel.Name"></label>
<input asp-for="ViewModel.Name" class="form-control" />
</div>
<div class="form-group">
<table class="table">
<thead>
<th>Key</th>
<th>Value</th>
</thead>
<tbody>
#for(int i = 0; i < Model.ViewModel.Resources.Count; i++) {
<tr>
<td>
<input asp-for="ViewModel.Resources[i].Key" type="hidden" />
#Model.ViewModel.Resources[i].Key
</td>
<td>
<input asp-for="ViewModel.Resources[i].Value" type="hidden" />
#Model.ViewModel.Resources[i].Value
</td>
</tr>
}
</tbody>
</table>
</div>
<button type="submit" class="btn btn-primary">Send</button>
</form>
Notice I've used type="hidden" because I didn't want the user to edit the table directly.
I hope you find this useful!

ASP.NET Editable Grid: Update register

I've been struggling with this for a while now. I'm constructing this view:
But when I hit the 'Update' button after do some changes the web refreshes to show the original values.
About the view: I get this view using an IEnumerable and looping thru each item in the model inside a form. Then, inside the form, there is a table that contains only 1 row. I do this in order to wrap all the items of the record in one form. This is part of code:
#foreach (var item in Model)
{
<form asp-action="Test" asp-route-id="#item.Id">
<table class="table">
<tbody>
<tr>
<td>
<input type="hidden" asp-for="#item.Id" />
<div class="form-group">
<div class="col-md-10">
<input asp-for="#item.MchName" readonly class="form-control" />
<span asp-validation-for="#item.MchName" class="text-danger"></span>
</div>
</div>
</td>
//more fields
<td>
<input type="submit" value="Update" class="btn btn-default" />
</td>
</tr>
</tbody>
</table>
</form>}
I declare an asp-action and a asp-route-id:
<form asp-action="Test" asp-route-id="#item.Id">
Question: Is this good enough? Is there something missing?
This is the Get Method:
public async Task<IActionResult> Test()
{
PopulateMachineTypeDropDownListStore();
return View(await _context.Machines.AsNoTracking().ToListAsync());
}
Question: I'm not passing any argument to the controller, yet the view will list the items following the given structure using an IEnumerable. Should I pass anything to the Get Method or is it fine as it is?
This is the Post Method:
#model IEnumerable<Application.Models.Machine>
[HttpPost, ActionName("Test")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> TestPost(int? id)
{
if (id == null)
{
return NotFound();
}
var machinetoUpdate = await _context.Machines
.SingleOrDefaultAsync(s => s.Id == id);
if (await TryUpdateModelAsync(
machinetoUpdate,
"",
s => s.MchName, s => s.StoreID, s => s.PUnit, s => s.Status))
{
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException)
{
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
return RedirectToAction("Test");
}
PopulateMachineTypeDropDownListStore();
return View(await _context.Machines.AsNoTracking().ToListAsync());
}
Question: I don't know if because the entity I retrieve the id from (and that I use to update the model thru TryUpdateModelAsync()) is also being used to compare to the model that thru the view this might not been working properly.
Thanks in advance for any help.

Validation for search input when model is a list

In the view, the model is:
#model List<ParamsTaskLib.ParamRecord>
I want to create a form with an input field which will be the DeviceID to search for:
public class ParamRecord
{
[Required]
[RegularExpression(#"^[0-9a-fA-F]{8}$", ErrorMessage = "Invalid Device ID (hex)")]
public Int64 DeviceID { get; set; }
other fields in here...
I tried editing the view in the following way:
<form method="get" >
#Html.EditorFor(model => model.First().DeviceID)
#Html.ValidationMessageFor(model => model.First().DeviceID)
But it doesn't work obviously, it doesn't seem like it SHOULD work:)
I'm confused with how to work with a list as a model, combined with a search box...
The error I get is "Value can't be NULL" on this line:
#Html.EditorFor(model => model.First().DeviceID)
Extra clarification: the purpose of this page is to display a table which contains many lines (ParamRecord) of a specific DeviceID from the database.
The purpose of the form + input field is to supply the web server with the DeviceID of which I want to see data (a collection of ParamRecord)
This is the complete View page:
#model List<ParamsTaskLib.ParamRecord>
#{
ViewBag.Title = "SearchResults";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script type="text/javascript" >
function UpdateFunc() {
$.get("/home/GetProgress", function(data) {
$("#UpdatePercentage").text(data);
if (data == "100")
location.reload();
else
setTimeout(UpdateFunc, 2000);
});
}
function Update() {
var id = $("#id").val();
$.post("#Url.Content("~/Home/UpdateID/")" + id, function (data) {
UpdateFunc();
});
$("#UpdateAni").attr("src", "#Url.Content("~/Content/Images/ajax-loader.gif")");
$("#UpdatePercentage").show();
}
</script>
<div>
<form method="get" >
#Html.TextBox("id")
#*#Html.EditorFor(model => model.First().DeviceID)*#
#Html.ValidationMessageFor(model => model.First().DeviceID)
<input type="submit" value="Search" />
<img id="UpdateAni" src="#Url.Content("~/Content/Images/ajax-loader_static.gif")" onclick="Update()"/>
<span id="UpdatePercentage" style="display:none" >0</span>
</form>
#if (Model != null )
{
<h4>ID 0x<span>#ViewBag.ID</span> , last update was at #ViewBag.LastUpdateTS</h4>
<h3>Total #ViewBag.NumOfParams parameters</h3>
<hr />
<table border="1" >
<thead>
<tr>
<th>Parameter Number</th>
<th>Device Inner ID</th>
<th>Device Type</th>
<th>Value (Int)</th>
<th>Value (Float)</th>
<th>Value (String)</th>
</tr>
</thead>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>#Model[i].ParamIndex</td>
<td>#Model[i].DeviceInnerID</td>
<td>#Model[i].DeviceType</td>
<td>#Model[i].ParamValInt</td>
<td>#Model[i].ParamValFloat</td>
<td>#Model[i].ParamValSTR</td>
</tr>
}
</table>
}
else if (ViewBag.NoResults == 1)
{
<h3>No results for ID 0x<span>#ViewBag.ID</span></h3>
}
</div>

Can only update the first record of a result set in MVC

I have taken over support of an MVC application, but unfortunately have pretty much zero experience or knowledge of MVC, so I apologise in advance if this is a stupid question.
We are placing every single result into a listed item in the HTML, then hiding all but one record, so you can filter through the different entries using First/Prev/Next/Last buttons, all via jQuery:
$("a#next").on("click", function () {
var nextli = $("li.first");
if ($(nextli).is(":last-child")) {
return false;
}
nextli.removeClass("first").addClass("record").next().addClass("first").removeClass("record");
});
$("a#previous").on("click", function () {
var nextli = $("li.first");
if ($(nextli).is(":first-child")) {
return false;
}
nextli.removeClass("first").addClass("record").prev().addClass("first").removeClass("record");
});
This is fine, and displays the records without any problem, however when you try to edit any record but the first, you get a "System.ArgumentNullException: Value cannot be null." error.
Here's the code in the controller for the edit function:
[HttpPost]
public ActionResult Edit(RecordsView rv)
{
if (ModelState.IsValid)
{
repository.EditRecord(rv.Records.FirstOrDefault().DogIncident);
}
return RedirectToAction("Index");
}
This is defined at the start of the cshtml file:
#using (Html.BeginForm("Edit", "Home", FormMethod.Post,new {#class="record-view"}))
And finally, here is how the HTML is generated on the view:
<li class="first" id="record-1805">
<form action="/Home/Edit" class="record-view" method="post">
<ul>
[form elements]
<li><input type="submit" style="margin: 18px 0;" value="Save"></li>
</ul>
</form>
</li>
<li class="record" id="record-1804">
<form action="/Home/Edit" class="record-view" method="post">
<ul>
[form elements]
<li><input type="submit" style="margin: 18px 0;" value="Save"></li>
</ul>
</form>
</li>
<li class="record" id="record-1803">
<form action="/Home/Edit" class="record-view" method="post">
<ul>
[form elements]
<li><input type="submit" style="margin: 18px 0;" value="Save"></li>
</ul>
</form>
</li>
Does anyone please know why I can only edit the first record that is displayed and not the others? Even if I go through the records using the next/back buttons and back to the first record to submit it, it updates fine, but updating any other record generates an error.
Referencing Darin's post: Pass a parameter to a controller using jquery ajax takes you halfway. He's using an input val in jQuery and passing it to the Controller Action.
Typically you'd provide one input link or #Html.ActionLink per record to be clicked for editing and MVC will bind to the controls parameter to your action (ID or what ever you want) just use the same name on the action link as the parameter and it will be mapped during the post.
data: { input: $('#caption').val() },
Change the signature of your controller action , or add another that takes an 'int' as shown below.
[HttpPost]
public ActionResult Edit(int ID = 0)
{
if (ModelState.IsValid)
{
repository.EditRecord(rv.Records.Where(r => r.ID == ID).DogIncident);
}
return RedirectToAction("Index");
}
Can you post the complete view so we can complete a solution ?
Was expecting the cshtml view. Here's an example:
#model IEnumerable<yourModelName>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.NID)
</th>
<th>
#Html.DisplayNameFor(model => model.CreatedOn)
</th>
<th>
#Html.DisplayNameFor(model => model.ExtensionAttribute15)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.yourElement1)
</td>
<td>
#Html.DisplayFor(modelItem => item.yourElement2)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
</td>
</tr>
}
</table>
#section Scripts{
<script type="text/javascript">
$("a#next").on("click", function () {
var nextli = $("li.first");
if ($(nextli).is(":last-child")) {
return false;
}
nextli.removeClass("first").addClass("record").next().addClass("first").removeClass("record");
});
$("a#previous").on("click", function () {
var nextli = $("li.first");
if ($(nextli).is(":first-child")) {
return false;
}
nextli.removeClass("first").addClass("record").prev().addClass("first").removeClass("record");
});
</script>
}

Resources