How to edit nested collections in MVC5? - entity-framework

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.

Related

controller post action not able catch list of objects

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)

Show single data column in ASP.NET MVC 5 using Entity Framework 6

Sorry if this question have exists.
I using ASP.NET MVC 5 + Entity Framework to build a project and I have a problem which not resolve.
In controller:
private NewsEntities db = new NewsEntities();
public ActionResult Index()
{
var art = db.Articles.Where(m=>m.Id==1).Select(m => new { m.Id , m.NameArt}).ToList();
return View(art);
}
And in view:
#model IEnumerable<DemoHC.Models.Article>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.NameArt)
</td>
<tr>
}
and I when build it in server then display error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[<>f__AnonymousType12[System.Int32,System.String]]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[DemoHC.Models.Article]'.
Can you help me and thanks pro
Your query is creating a collection of anonymous objects, not a collection of Article objects.
Change your code to
public ActionResult Index()
{
var art = db.Articles.Where(m => m.Id == 1);
return View(art);
}
or alternatively you could create a view model with just the properties you want
public class ArticleVM
{
public int ID { get; set; }
public string Name { get; set; }
}
and change the query to
var art = db.Articles.Where(m => m.Id == 1).Select(m => new ArticleVM
{
ID = m.Id,
Name = m.NameArt
};
return View(art);
and the model in the view will then be
#model IEnumerable<ArticleVM>

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);
}

ModelBinder NHibernate Complex Object with Collections

Below, on initial load (Edit), everything displays fine. However, when I POST (Action below), it looks like it goes and tries to grab the ProductAttribute models separately, with the Product's id, which promptly fails. How to keep my Binder implementation from trying to re-bind Collections as separate entities?
Thanks!
Model
public class Product {
virtual public long Id { get; set; }
virtual public string Name { get; set; }
private IList<ProductAttribute> _productAttributes;
public virtual IList<ProductAttribute> ProductAttributes {
get{
if(_productAttributes == null){
_productAttributes = new List<ProductAttribute>();
}
return _productAttributes;
}
set{
_productAttributes = value;
}
}
}
View
<%using (Html.BeginForm(new {id = Model.Id > 0 ? (long?)Model.Id : null})) {%>
<table class="form">
<% for(var i=0; i < Model.ProductAttributes.Count; i++){
var pa = Model.ProductAttributes[i]; %>
<tr>
<th><%: Model.ProductAttributes[i].Name %></th>
<td>
<%: Html.Hidden("ProductAttributes.Index", i) %>
<% if(pa.CanSpecifyValueDirectly()){ %>
<%: Html.TextBoxFor(model => model.ProductAttributes[i].Value) %>
<% } else { %>
<%: Html.DropDownListFor(model => model.ProductAttributes[i].Value, new SelectList(pa.MarketAttribute.AttributeLevels, "id", "Name", pa.AttributeLevel)) %>
<% } %>
</td>
</tr>
<input type="submit" value="Save" />
</table>
<%}%>
Controller
public ActionResult Edit(long id, Product product) {
ViewData.Model = product;
if (ModelState.IsValid) {
var results = product.Update();
ViewData["results"] = results;
if (!results.Error) {
return RedirectToAction("Show", new { id = id });
}
}
return View();
}
Binder
public class StackModelBinder : DefaultModelBinder {
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) {
var modelInterface = modelType.GetInterface("IModel");
if (modelInterface == null) {
return base.CreateModel(controllerContext, bindingContext, modelType);
}
var value = bindingContext.ValueProvider.GetValue("id").RawValue.ToString();
if (string.IsNullOrEmpty(value)) {
return base.CreateModel(controllerContext, bindingContext, modelType);
}
var id = Convert.ChangeType(value, typeof (long));
var assembly = Assembly.GetAssembly(modelType);
var dao = assembly.GetType(string.Concat(assembly.GetName().Name, ".core.GlobalDao`1[", modelType.FullName, "]"));
if (dao == null) {
return base.CreateModel(controllerContext, bindingContext, modelType);
}
var method = dao.GetMethod("Get");
return method.Invoke(null, new[] {id});
}
}
Typically, it is a bad idea to push entities through the model binder--they tend to be a bit too complex for it to handle, never mind the exciting stuff that goes on in modern ORMs, such as dynamic proxies, that can give the ModelBinder or the ORM fits.
Your best bet here is to change the rules and build a dedicated class for taking the edits and transferring that to the controller. This class can be ModelBinder-friendly and you get the added benefit of separating the UI from the domain entities.