controller post action not able catch list of objects - entity-framework

It may duplicate question, i have searched all over but couldn't satisfied, so i am posting here question.
I have object as (generated from entity framework),
public partial class Usp_Sel_NotEnteredStringResources_Result
{
public string name { get; set; }
public Nullable<int> Value { get; set; }
public int StringResourceId { get; set; }
public Nullable<int> LanguageId { get; set; }
}
and view i have created as,
#{
ViewBag.Title = "Resource Entry Languagewise";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>ResourceEntry</h2>
#using (Html.BeginForm("ResourceEntry", "LangResource", FormMethod.Post))
{
<fieldset>
<table>
#for (int i = 0; i < Model.ToList().Count; i++)
{
<tr>
<td>#Html.DisplayFor(m => Model.ElementAt(i).name)</td>
<td>#Html.TextBoxFor(m => Model.ElementAt(i).Value)</td>
#Html.HiddenFor(m => Model.ElementAt(i).LanguageId)
#Html.HiddenFor(m => Model.ElementAt(i).name)
#Html.HiddenFor(m => Model.ElementAt(i).StringResourceId)
</tr>
}
</table>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
And Controller as,
[HttpPost]
public ActionResult ResourceEntry(List<Usp_Sel_NotEnteredStringResources_Result> list)
{
// here getting the null value for list
// ??????
return View(list);
}
After submitting the form controller gets null value for list, what is wrong with code??

You cannot use .ElementAt(). If you inspect the html your generating you will see that the name attribute has no relationship to your model.
You model needs to implemet IList<Usp_Sel_NotEnteredStringResources_Result> and use a for loop
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>#Html.DisplayFor(m => m[i].name)</td>
<td>
#Html.TextBoxFor(m => m.[i]Value)
// note you hidden inputs should be inside a td element
#Html.HiddenFor(m => m[i].LanguageId)
....
</td>
</tr>
}
Alternative if the model is IEnumerable<Usp_Sel_NotEnteredStringResources_Result>, you can use an EditorTemplate (refer [Post an HTML Table to ADO.NET DataTable for more details on how the name attributes must match your model property names, and the use of an EditorTemplate)

Related

Insert into DB from ASP.Net form

I have an issue when I want to insert an object into the database.
My model is Colis class which has a foreign key to ZoneReserve (ZoneReserveId), which has a foreign key on Reserve (ReserveId).
In my form I choose an existing ZoneReserve and Reserve, but when I post my form, new lines are created in DB, in table ZoneReserve and Reserve. Entity framework do not retrieve the existing line or I don't know...
I don't know if I'm clear enough, sorry for my english ;)
Do you have any advice ? I'm stuck et I tried everything :(
Thank you guys
Colis Model Class :
public class Colis
{
public int ColisId { get; set; }
[Display(Name = "Code barre du colis")]
public string CodeBarreColis { get; set; }
public bool IndAVendreColis { get; set; }
public virtual TypeColis TypeColis { get; set; }
[Display(Name = "Type de colis")]
public int TypeColisId { get; set; }
public ZoneReserve ZoneReserve { get; set; }
[Display(Name = "Emplacement du colis")]
public int ZoneReserveId { get; set;
}
ZoneReserve Model Class :
public class ZoneReserve
{
public int Id { get; set; }
public string NomZoneReserve { get; set; }
public Reserve Reserve { get; set; }
public int ReserveId { get; set; }
}
Reserve Model Class :
public class Reserve
{
[Display(Name = "Réserve")]
public int Id { get; set; }
public string NomReserve { get; set; }
}
My Action in ColisController :
[HttpPost]
public ActionResult CreerColis(Colis colis)
{
_context.Colis.Add(colis);
_context.SaveChanges();
return RedirectToAction("ListeColis");
}
My Form in the view :
#using (Html.BeginForm("CreerColis", "Colis"))
{
<div class="form-group">
#Html.LabelFor(m => m.Colis.CodeBarreColis)
#Html.TextBoxFor(m => m.Colis.CodeBarreColis, new { #class = "form-control" })
</div>
<div class="checkbox">
<label>
#Html.CheckBoxFor(m => m.Colis.IndAVendreColis) A vendre ?
</label>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Colis.TypeColisId)
#Html.DropDownListFor(m => m.Colis.TypeColisId, new SelectList(Model.TypeColis, "Id", "NomTypeColis"), "Selectionner un type", new { #class = "form -control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserve.Reserve.Id)
#Html.DropDownListFor(m => m.Colis.ZoneReserve.Reserve.Id, new SelectList(Model.Reserve, "Id", "NomReserve"), "Selectionner une zone", new { #class = "form -control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserveId)
#Html.DropDownListFor(m => m.Colis.ZoneReserve.Id, new SelectList(Model.ZoneReserve, "Id", "NomZoneReserve"), "Selectionner une zone", new { #class = "form -control" })
</div>
<button type="submit" class="bt, btn-primary">Enregistrer</button>
}
<script src="~/Scripts/jquery-3.4.1.min.js"></script>
<script>
$(document).ready(function () {
$("#Colis_ZoneReserve_Reserve_Id").change(function () {
$.get("/ZoneReserve/ListeZoneReserveParReserve", { ReserveId: $("#Colis_ZoneReserve_Reserve_Id").val() }, function (data) {
$("#Colis_ZoneReserve_Id").empty();
$.each(data, function (index, row) {
$("#Colis_ZoneReserve_Id").append("<option value='" + row.Id + "'>" + row.NomZoneReserve+ "</option>")
});
});
})
});
</script>
It looks like your razor page is posting info about navigation properties of the Colis object to the controller and creating the full objects instead of creating a new Colis object with just the int foreign key specified.
As is, when posted, '''colis.ZoneReserve''' is not null nor is '''colis.ZoneReserve.Reserve''' reference which tells entity framework to create those related object as well when you .Add(colis) to the context.
[HttpPost]
public ActionResult CreerColis(Colis colis)
{
_context.Colis.Add(colis);
_context.SaveChanges();
return RedirectToAction("ListeColis");
}
You are POSTing unintended parameters to your controller, specifically '''Colis.ZoneReserve.Id''' and '''Colis.ZoneReserve.Reserve.Id''' as you BOUND TO in your razor page (see comments in code):
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserve.Reserve.Id)
<!-- DropDownListFor m.Colis.ZoneReserve.ReserveId will send that (navigation path) value to the server. //-->
#Html.DropDownListFor(m => m.Colis.ZoneReserve.Reserve.Id, new SelectList(Model.Reserve, "Id", "NomReserve"), "Selectionner une zone", new { #class = "form -control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserveId)
<!-- DropDownListFor m.Colis.ZoneResearch.Id will send that navigation property to the server //-->
#Html.DropDownListFor(m => m.Colis.ZoneReserve.Id, new SelectList(Model.ZoneReserve, "Id", "NomZoneReserve"), "Selectionner une zone", new { #class = "form -control" })
</div>
To fix your razor page (and not send unintended values to the server)
change the first drop down list for Reserve to NOT be for anything it'll bind to on the server (you don't even need to POST it's value if you can strip it before submit), one way is to change it's name to something meaningless such as "UnnecessaryData" that won't map in the controller when posted (pseudo-code, not tested)
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserve.Reserve.Id)
#Html.DropDownList(new SelectList(Model.Reserve, "Id", "NomReserve"),
"Selectionner une zone",
new { #class = "form-control", name = "UnnecessaryData" })
</div>
Change the second drop-down-list to map to the correct property on the Colis object, notice all I did was change m => m.Colis.ZoneReserve.Id to the FK property of Colis m => m.Colis.ZoneReserveId:
<div class="form-group">
#Html.LabelFor(m => m.Colis.ZoneReserveId)
#Html.DropDownListFor(m => m.Colis.ZoneReserveId, new SelectList(Model.ZoneReserve, "Id", "NomZoneReserve"), "Selectionner une zone", new { #class = "form -control" })
</div>
When you POST the form, your Colis object in the controller should have a NULL ZoneReserve property and a non-zero ZoneReserveId property - this will prevent the other data records from being created by entity framework.
Note: You can also simply strip the data from the Colis in the POST controller - but that doesn't correct your implementation on the client razor page that's sending unintended structure to the server in the POST method.
Also note: Because you don't validate that the navigation properties of Colis are NULL in the controller, a malicious user COULD create a lot of crap data on the server by POSTing full object tree data that'll be added with the controller method as implemented.

How to edit nested collections in MVC5?

I have a EF-model which contains a "key" and a "value". The value-table contains a FK to the key. In the EF-model it looks like this:
public partial class dict_key
{
public dict_key()
{
this.dict_value = new HashSet<dict_value>();
}
public int id { get; set; }
public string name { get; set; }
...
public virtual ICollection<dict_value> dict_value { get; set; } //dict_value contains a string "value"
}
My controller is passing the information for editing like this:
// GET: Keys/Texts/5
[Authorize]
public async Task<ActionResult> Texts(int? id)
{
var key = await db.dict_key
.Include(x => x.dict_value)
.Where(x => x.id.Equals(id.Value))
.FirstOrDefaultAsync();
return View(key);
// Debugging 'key' shows that dict_value has 3 correct values.
}
This gets passed to my View which shows the dict_value's correct:
#model Dict.Models.dict_key
#using (Html.BeginForm())
{
<div>Key: #Model.name </div>
<table class="table">
<tr>
<th>Language</th>
<th>Text</th>
</tr>
#for (var i = 0; i < Model.dict_value.Count(); i++)
{
<tr>
<td> #Model.dict_value.ElementAt(i).dict_lang.name_en </td>
<td> #Html.EditorFor(x => x.dict_value.ElementAt(i).value) </td>
</tr>
}
<div class="form-group">
<input type="submit" value="Save" />
</div>
</table>
}
When submitting my changes back to the controller...
[HttpPost]
public async Task<ActionResult> Texts(dict_key dict_key)
{
if (ModelState.IsValid)
{
//Also tried: db.Entry(dict_key).State = EntityState.Modified;
db.Entry(dict_key.dict_value).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Texts");
}
return View(dict_key);
}
..then my "dict_key" is totally different from the object I passed to my edit-view. The passed object contained the collection of dict_value's and the "returned" and edited object returns with the proper key object, but with an empty dict_value collection.
I try to avoid using a userdefined model or the viewbag to do all of that stuff manually. What is the best practise solution for this?
Collection.ElementAt doesn't generate a proper field name in Razor. You need a List. Here you should use a view model instead of your entity directly and simply make your dict_value collection a List<dict_value> there.
Alternatively, you can create an editor template for dict_value and then in your view just do:
#Html.EditorFor(m => m.dict_value)
Where dict_value there is your entire collection. Razor will render an instance of the editor template for each member of the collection and properly index everything.

Insert Data to tblEducation using asp.net mvc 2.0

I have 2 tables in database:tbleducation,tblemployee.Anyway for table tblemployee, I have fields: employeeID,employeeName....I want to insert data into tbleducation that have fields such as:EmployeeID,Duration,.....And for the EmployeeID of table tbleducation I want to do the dropdownlist that list all EmployeeName in tblEmployee into dropdownlist.And I have code as below:
View
<div id="Education">
<%Html.EnableClientValidation(); %>
<% using (Html.BeginForm("Education","EmployeeProfile")){%>
<% Html.ValidationSummary(true, "Unsuccessfull"); %>
<table>
<tr>
<td>Employee Name</td>
<td><%= Html.DropDownListFor(x => x.EmployeeName, Model.Employee, "select EmployeeName")%</td>
<td>Duration</td>
<td><%: Html.TextBoxFor(m=> m.Duration, new { id="duration",name="duration"})%>
<%: Html.ValidationMessageFor(m => m.Duration) %></td>
<tr>
<td><input type="submit" name="add" id="add" value="Add" /></td>
</tr>
</table>
<%}%>
</div>
Model
public class UserModels
{
public string EmployeeName { get; set; }
public int EmployeeCode { get; set; }
public IEnumerable<SelectListItem> Employee { set; get; }
}
Controller
public ActionResult Education() {
var query = (from e in context.tblEmployee_Employee
select new
{
empID = e.Code,
EmpName = e.NameEng
}
).ToList();
var model = new UserModels();
var _Emp = query;
foreach (var item in _Emp)
{
model.EmployeeCode = item.empID;
model.EmployeeName = item.EmpName;
model.Employee = new SelectList(_Emp, "EmpName", "EmpName");
}
return View(model);
}
[HttpPost]
public ActionResult Education(UserModels model, FormCollection edu) {
tblEmployee_Education education = new tblEmployee_Education();
education.Duration = edu["Duration"].ToString();
education.Certificate = edu["Certificate"].ToString();
education.Country = edu["Country"].ToString();
education.SchoolName = edu["SchoolName"].ToString();
education.Major = edu["Major"].ToString();
education.SubDescript = edu["SubDescript"].ToString();
string EName = edu["EmployeeName"].ToString();
return Content(
string.Format(
"Selected role for {0} is {1}", model.EmployeeName, model.EmployeeCode
)
);
context.AddTotblEmployee_Education(education);
context.SaveChanges();
return View("Personal");
}
I got the warning message "unreachble code detected".I really do not know how to solve it.Please kindly help me,
Thanks in advanced,
In your POST action, here:
return Content(
string.Format(
"Selected role for {0} is {1}", model.EmployeeName, model.EmployeeCode
)
);
you have already returned a result and the action execution ends.
So get rid of the lines that follow it, they will never be executed:
context.AddTotblEmployee_Education(education);
context.SaveChanges();
return View("Personal");
And if you want them to be executed, then remove the return Content(...) line.

LINQ to Entities error when trying to bind a dropdownlist

I am trying to create a viewmodel and add a SelectListItem to allow me to bind a dropdown list.
I have a very basic viewmodel that looks like this
public class CreatePurchaseViewModel
{
public Product Product { get; set; }
public IEnumerable<SelectListItem> Products { get; set; }
public int SelectedProductId { get; set; }
public DateTime OrderDate { get; set; }
public bool OrderSent { get; set; }
}
My controller looks like this
[HttpGet]
public ActionResult Create()
{
var model = new CreatePurchaseViewModel
{
Products = context.Products.Select(x =>
new SelectListItem()
{
Text = x.ProductName,
Value = x.ProductID
})
};
return View(model);
}
However it complains that Value = x.Product cant convert type int to string. So if I add a .ToString it compiles ok but when I try load the view I get an error
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
My View
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>CreatePurchaseViewModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.SelectedProductId)
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.SelectedProductId,Model.Products)
#Html.ValidationMessageFor(model => model.SelectedProductId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.OrderDate)
</div>
<div class="editor-field">
#Html.TextBoxFor(model=>model.OrderDate)
#Html.ValidationMessageFor(model => model.OrderDate)
</div>
<div>
Sent
#Html.RadioButtonFor(model => model.OrderSent, "Sent", new { #checked = true })
Not Sent
#Html.RadioButtonFor(model=>model.OrderSent,"Not Sent")
Im pretty new to both entity framework and mvc so any help would be great.
Thank you
You haven't specified how does your Product model look like but we can assume that the ProductID property is integer so you might need to convert it to a string:
[HttpGet]
public ActionResult Create()
{
var model = new CreatePurchaseViewModel
{
Products = context.Products.Select(x =>
new SelectListItem
{
Text = x.ProductName,
Value = x.ProductID.ToString()
}
)
};
return View(model);
}

Using ModelMetadata how do you call DisplayFor and have the metadata used

This question is based on using the Asp.Net MVC tabular layout display template from Phil Haack http://haacked.com/archive/2010/05/05/asp-net-mvc-tabular-display-template.aspx
The problem is in the Html.DisplayFor(m => propertyMetadata.Model). This displays the data from the property just fine, however it doesn't use any of the data annotations that may be present. I'm mostly thinking the DataType annotation here for example DataType.Date. This annotation correctly outputs a short date when used with DisplayFor, but not when the property is reflected from the ModelMetadata as in this example. (It shows a full DateTime)
<% for(int i = 0; i < Model.Count; i++) {
var itemMD = ModelMetadata.FromLambdaExpression(m => m[i], ViewData); %>
<tr>
<% foreach(var property in properties) { %>
<td>
<% var propertyMetadata = itemMD.Properties
.Single(m => m.PropertyName == property.PropertyName); %>
<%= Html.DisplayFor(m => propertyMetadata.Model) %>
</td>
<% } %>
</tr>
An example model would be an
public class TableModel
{
[UIHint("Table")]
public PeriodModel[] Periods { get; set; }
}
public class PeriodModel
{
[DisplayName("Description")]
public string Description { get; set; }
[DisplayName("Date From")]
[DataType(DataType.Date)]
public DateTime DateFrom { get; set; }
[DisplayName("Date To")]
[DataType(DataType.Date)]
public DateTime DateTo { get; set; }
}
So how would this be changed to get the full metadata behaviour of DisplayFor?
Seems this is a simple one, you just have to change:
Html.DisplayFor(m => propertyMetadata.Model)
to
Html.DisplayFor(m => propertyMetadata.Model, propertyMetadata.TemplateHint)
I've been struggling with this myself for a few hours and this is the only hit I found on Google. But the answer is wrong.
propertyMetadata.TemplateHint is always null in my case.
But this works:
Html.DisplayFor(m => propertyMetadata.Model, propertyMetadata.DataTypeName)
you can check #Html.ValueFor(m => propertyMetdata.Model) instead of DisplayFor.