I am new to ASP.NET MVC and I am kindly asking you to help me with this problem:
I have 2 models - Delegation:
public int idDelegation { get; set; }
public string Delegation_Name { get; set; }
public string Employee_Name { get; set; }
and Project:
public int idProject { get; set; }
public string Project_System_Name { get; set; }
public string Project_System_ID { get; set; }
I want to display in a view a list of current delegations and projects, with the Edit/Details/Delete options for each one.
I created a ViewModel DelegationProject:
public class DelegationProject
{
public IEnumerable<Delegation> delegations { get; set; }
public IEnumerable<Project> projects { get; set; }
}
In my Index view I have:
#model IEnumerable<....ViewModel.DelegationProject>
<table class="table">
<tr>
<th>#Html.DisplayNameFor(model => model.delegations.Employee_Name</th>
<th>#Html.DisplayNameFor(model => model.delegations.Delegation_Name</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(itemModel => item.delegations.Employee_Name)</td>
<td>#Html.DisplayFor(itemModel => item.delegations.Delegation_Name</td>
<td>
#Html.ActionLink("Edit", "Edit", new { x => x.idDelegation }) |
#Html.ActionLink("Details", "Details", new { x => x.idDelegation }) |
#Html.ActionLink("Delete", "Delete", new { x => x.idDelegation })
</td>
</tr>
}
</table>
When I run the application, I get these error messages:
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS1061: 'System.Collections.Generic.IEnumerable' does not contain a definition for 'Employee_Name' and no extension method 'Employee_Name' accepting a first argument of type 'System.Collections.Generic.IEnumerable' could be found (are you missing a using directive or an assembly reference?)
I tried different things (like List<> or Tuple) from stackoverflow topics and other sites posts, but I wasn't able to use to models in the same view and iterate through them.
I managed to find a solution for this problem.
The ViewModel DelegationProject remained the same, in my controller for Index action I have:
public ActionResult Index()
{
DelegationProject viewModel = new DelegationProject();
viewModel.delegations = GetDelegations();
viewModel.projects = GetProjects();
return View(viewModel);
}
And I created two methods in the respective controller - GetDelegations() and GetProjects():
public List<Delegation> GetDelegations()
{
var deleg = db.Delegation.Include(d => d.Project);
var list = new List<Delegation>(deleg).ToList();
return list;
}
public List<Project> GetProjects()
{
var projects = db.Project.Include(p => p.Country);
var list = new List<Project>(projects);
return list;
}
My View looks like this:
#model .......ViewModel.DelegationProject
#foreach (var item in Model.delegations)
{
<tr>
<td>
#Html.DisplayFor(itemModel => item.Employee_Name)
</td>
<td>
#Html.DisplayFor(itemModel => item.Delegation_Name)
</td>
</tr>
}
Hope this could be of any help to somebody.
Related
I have 2 models: (1) FamiliaMáquina (PK: FamiliaId) and (2) TipoMáquina (PK: TipoMáquinaId).
The relation between these is: TipoMáquina can have only 1 FamiliaId associated, while FamiliaMáquina can have many TipoMáquinaId associated (one to many relationship).
FamiliaMáquina definition:
public class FamiliaMáquina
{
[Key, Required]
public int FamiliaId { get; set; }
public string DescripciónFamilia { get; set; }
public virtual ICollection<TipoMáquina> TipoMáquina { get; set; }
}
TipoMáquina definition:
public class TipoMáquina
{
[Key, Required]
public int TipoMáquinaId { get; set; }
public string DescripciónTipoMáq { get; set; }
public int FamiliaId { get; set; }
public virtual FamiliaMáquina FamiliaMáquina { get; set; }
}
The objetive: Show, programing the FamiliaMáquina controller, a list of FamiliaId (from model 1) and DescripciónTipoMáq (from model 2) associated. For this, the tables have been populated with sample data.
On the FamiliaMaqController (where the association takes places - eager loading):
public class FamiliaMaqController : Controller
{
private ApplicationDbContext _context;
public FamiliaMaqController()
{
_context = new ApplicationDbContext();
}
protected override void Dispose(bool disposing)
{_context.Dispose();}
public async Task<ActionResult> NuevaMQ()
{
var fammáquinas = _context.FamiliaMáquinas.Include(c => c.TipoMáquina).AsNoTracking();
return View(await fammáquinas.ToListAsync());
}
}
This is supossed to show on NuevaMQ.cshtml. NuevaMQ code is:
#model IEnumerable<Plataforma.Models.FamiliaMáquina>
#{
ViewBag.Title = "NuevaMQ";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>NuevaMQ</h2>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Familia de Máquina</th>
<th>Tipo de Máquina</th>
</tr>
</thead>
<tbody>
#foreach (var fammáquinas in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => fammáquinas.FamiliaId)</td>
<td>#Html.DisplayFor(modelItem => fammáquinas.TipoMáquina.DescripciónTipoMáq)</td>
</tr>
}
</tbody>
</table>
I'm getting an error on this line:
#Html.DisplayFor(modelItem => fammáquinas.TipoMáquina.DescripciónTipoMáq
Supposedly, FamiliaID and DescripciónTipoMáq should be listed, however, I get the following warning:
"ICollection doesn't have a definition for 'DescripciónTipoMáq'."
¿Why isn't eager loading working? I've been struggling with this for a while now. It's like this line of code:
var fammáquinas = _context.FamiliaMáquinas.Include(c => c.TipoMáquina).AsNoTracking();
Is not associating the tables but I'm not getting an error there.
What I'm getting as a result in the website is:
FamiliaId: 1
DescripciónTipoMáq: System.Data.Entity.DynamicProxies.FamiliaMáquina_EB64915DB33A48C74F57D392EFE55D5AEDC5BF74BE67B819F4737EEB09B6F436
This repeats as many times as items related per FamiliaId. It's like the program is iterating as it should but getting that reference everytime instead of the field value.
You've the error in that line
#Html.DisplayFor(modelItem => fammáquinas.TipoMáquina.DescripciónTipoMáq)
Because based on the code of FamiliaMáquina class you're using TipoMáquina which is a collection ICollection<TipoMáquina>. And ICollection<TipoMáquina> likee all ICollection<T> doesn't define DescripciónTipoMáq property that is what the error said:
ICollection doesn't have a definition for 'DescripciónTipoMáq'.
So if you wan't to get the description you need to iterate through each TipoMáquina item and display DescripciónTipoMáq property
Side Note: Avoid using that kind of naming things with accent because you or another developper can lost a lot of time to know why the code doesn't compile and will find that after several minutes that he or she mispelled the name of the varaible.
It worked!
I've changed the controller in this way:
public async Task<ActionResult> NuevaMQ()
{
var fammáquinas = _context.TipoMáquinas.Include(c => c.FamiliaMáquina).AsNoTracking();
return View(await fammáquinas.ToListAsync());
}
Then, I went to the view to change it accordingly so I could invok the data from FamiliaMáquina and brind the FamiliaId:
<tbody>
#foreach (var fammáquinas in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => fammáquinas.FamiliaId)</td>
<td>##Html.DisplayFor(modelItem => fammáquinas.DescripciónTipoMáq)</td>
</tr>
}
</tbody>
also, changed the model of reference at top of the cshtml file:
#model IEnumerable<Plataforma.Models.TipoMáquina>
And as result, the table showed both atributes invoked from diferent tables :) Thanks for the help!
I need a view that displays rows of header-detail info. For a trivial case I set up a Course-Student tiny database with 4 simple tables. The Courses and Students tables are linked with a Section table which also has a student grade in it. Also, the Student table is owned by a Student Type Table. There are helper classes to hold the list of "header" data for the course and a "detail" list for each student who takes that class.
namespace SchoolA.Models
{
public class CourseInfo2
{
public int CourseID { get; set; }
public int CourseNumber { get; set; }
public string CourseName { get; set; }
public IEnumerable<CourseSectionList> CourseStudentList { get; set; }
}
}
namespace SchoolA.Models
{
public class CourseSectionList
{
public string Student { get; set; }
public string Type { get; set; }
public string Grade { get; set; }
}
}
The controller is:
public ActionResult Courselisting()
{
List<CourseInfo2> courseInfo2 = new List<CourseInfo2>();
List<CourseSectionList> courseSectionList = new List<CourseSectionList>();
var Cquery = from c in db.Courses select c;
foreach (var item in Cquery)
{
Course course = db.Courses.Find(item.CourseID); // first find the selected course
// get the sections
foreach (var s in query) // go through each section
{
// get the section data
courseSectionList.Add(new CourseSectionList
{
Student = student.StudentName,
Type = studentType.StudentTypeName,
Grade = s.SectionStudentGrade
});
} // end of section loop
courseInfo2.Add(new CourseInfo2
{
CourseID = course.CourseID,
CourseNumber = course.CourseNumber,
CourseName = course.CourseName,
CourseStudentList = courseSectionList
});
} // end of Course loop
return View(courseInfo2); // Course List and Section list for each course
}
The View is:
#model SchoolA.Models.CourseInfo2
#* doesn't work either: model List<SchoolA.Models.CourseInfo2>*#
<div>
<table>
<tr><th>ID</th> <th>Number</th> <th>Course</th></tr>
#foreach (var CourseInfo2 in Model)
{
<tr>
<td>
#CourseInfo2.CourseID
</td>
<td>
#CourseInfo2.CourseNumber
</td>
<td>
#CourseInfo2.CourseName
</td>
<td>
<table class="table">
<tr><th>Student</th> <th>Type</th><th>Grade</th></tr>
#foreach (var s in Model.CourseStudentList)
{
<tr>
<td>
#s.Student
</td>
<td>
#s.Type
</td>
<td>
#s.Grade
</td>
</tr>
}
</table>
</td>
</tr>
}
</table>
</div>
The problem is that I get a variety of errors as I try to pass these two models to the view. In the code as shown above I get this error:
CS1579: foreach statement cannot operate on variables of type 'SchoolA.Models.CourseInfo2' because 'SchoolA.Models.CourseInfo2' does not contain a public definition for 'GetEnumerator'
I've tried a number of variations for passing the models but always run into one error other the other that prevents both models from working in the view. I should note that I tested each part of the view independently and the controller code works fine to deliver the correct data to the view. However, I can't combine the two.
The problem seems to be the way I create instances of the models an how they are passed to the view. What am I doing wrong?
You're passing your view a List<SchoolA.Models.CourseInfo2>, but the view expects a single SchoolA.Models.CourseInfo2.
Change your #model declaration to IEnumerable<SchoolA.Models.CourseInfo2>.
Then change your foreach loops.
Change the first loop to foreach (var courseInfo in Model)
Change the inner loop to foreach (var s in courseInfo.CourseStudentList)
You are passing a model which is list of object, while in view you have single object.
Change your view model bind as below:
#model List<SchoolA.Models.CourseInfo2>
Problem: I have to edit Level2Name (by UI it's a textbox inside the child grid).
I'm able to edit Level1name (parent grid text field) and able to get value in my controller.
Question: How do I able to edit the nested textbox.
What I've tried.
Code:
Model
public class Level0
{
public Level0()
{
Level1= new List<Level1>();
}
public int? ID{ get; set; }
public int? NameText{ get; set; }
public List<Level1> lev1{ get; set; }
}
public class Level1
{
public Level1()
{
Level2= new List<Level2>();
}
public int Lev1ID {get;set;}
public string Level1name{ get; set; }
public List<Level2> level2{ get; set; }
}
public class Level2
{
public string Level2_ID { get; set; }
public string Level2Name{ get; set; }
}
UI Code
#for (int i = 0; i < Model.Level1.Count; i++)
{
#Html.HiddenFor(modelItem => Model.Floors[i].Lev1ID )
#for (int j = 0; j < Model.Level1[i].Level2.Count; j++)
{
#Html.HiddenFor(m => m.Level1[i].Level2[j].OP)
#Html.TextBoxFor(m => m.Level1[i].Level2[j].Level2Name, new { #class = "form-control" })
}
}
I have two classes generated from my database by EF 6:
class Person {
public int PID { set; get; }
public string Name { set; get; }
public int CountryID { set; get; }
public virtual ICollection<Country> Country { set; get;}
}
class Country {
public int CID { set; get; }
public string Name { set; get; }
}
I would like to show data in a table as follows:
Person Name ^v Countries ^v Operation
----------------------------- ----------------------------- ---------------
[___________________________] [________dropdown_________|v] [Edit] [Delete]
...
[___________________________] [________dropdown_________|v] [Edit] [Delete]
[Pager]
In my database CountryID is a foreign key to Country.CID. I would like to show data and allow to in line editing and/or deleting. The columns have to be disabled but in edit mode of course enabled instead.
I googled a lot and read stackoverflow too but didn't found a comprehensive help for this.
Does anybody has an example how to do this gridview?
Thanks a lot
You can try this:
Controller
public ActionResult Index()
{
var persons = db.Person.Include(c => c.Country);
return View(persons.ToList());
}
View
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.PersonName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Country.CountryName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.PersonID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.PersonID })
</td>
</tr>
http://www.jtable.org/demo/PagingAndSorting
You can also find some good nuget packages for inline editing grid.
I am trying to use two models in one view, but from what I understand my program just don't see any objects within models.
Here is my code.
Models:
public class Album
{
[Key]
public int ThreadId { get; set; }
public int GenreId { get; set; }
public string Title { get; set; }
public string ThreadByUser { get; set; }
public string ThreadCreationDate { get; set; }
public string ThreadContent { get; set; }
public Genre Genre { get; set; }
public List<Posts> Posty { get; set; }
}
public class Posts
{
[Key]
public int PostId { get; set; }
public int ThreadId { get; set; }
public string PostTitle { get; set; }
public string PostContent { get; set; }
public string PostDate { get; set; }
public string PosterName { get; set; }
public Album Album { get; set; }
}
public class ModelMix
{
public IEnumerable<Posts> PostsObject { get; set; }
public IEnumerable<Album> ThreadsObject { get; set; }
}
Index controller code:
public ActionResult Index(int id)
{
ViewBag.ThreadId = id;
var posts = db.Posts.Include(p => p.Album).ToList();
var albums = db.Albums.Include(a => a.Genre).ToList();
var mixmodel = new ModelMix
{
PostsObject = posts,
ThreadsObject = albums
};
return View(mixmodel);
}
View code:
#model MvcMusicStore.Models.ModelMix
<h2>Index</h2>
#Html.DisplayNameFor(model => model.PostsObject.PostContent)
And when I try to execute my program I am getting this error:
CS1061: The "
System.Collections.Generic.IEnumerable 'does not contain a definition
of" PostContent "not found method of expanding" PostContent ", which
takes a first argument of type' System.Collections.Generic.IEnumerable
"
How I can make it work as intended? There are a lot of questions like mine on the internet but I couldn't find any matching my case.
Looping over models can be a little confusing to begin with in MVC, only because the templated helpers (i.e. Html.DisplayFor and Html.EditorFor) can be provided templates which the helper will automatically invoke for every element in a collection. That means if you're new to MVC, and you don't realise a DisplayTemplate or an EditorTemplate has not been provided for the collection already, it looks as though a simple:
#Html.DisplayFor(m => m.SomePropertyThatHoldsACollection)
is all you need. So if you've seen something like that already, that might be why you made the assumption it would work. However, let's assume for a moment that a template has not been provided. You have two options.
Firstly, and most simply, would be to use foreach over the collection:
#foreach (var post in Model.PostsObject)
{
#Html.DisplayFor(m => post.PostTitle)
// display other properties
}
You could also use a for loop, but with IEnumerable<T>, there is no indexer, so this won't work:
#for (int i = 0; i < Model.PostsObject.Count(); i++)
{
// This generates a compile-time error because
// the index post[i] does not exist.
// This syntax would work for a List<T> though.
#Html.DisplayFor(m => post[i].PostTitle)
// display other properties
}
If you did want to use the for loop still, you can use it like so:
#for (int i = 0; i < Model.PostsObject.Count(); i++)
{
// This works correctly
#Html.DisplayFor(m => post.ElementAt(i).PostTitle)
// display other properties
}
So use whichever you prefer. However, at some point it would be a good idea to look into providing templates for your types. (Note: Although this article was written for MVC 2, the advice still applies.) They allow you to remove looping logic from your views, keeping them cleaner. When combined with Html.DisplayFor, or Html.EditorFor, they will also generate correct element naming for model binding (which is great). They also allow you to reuse presentation for a type.
One final comment I'd make is that the naming of your properties is a little verbose:
public class ModelMix
{
public IEnumerable<Posts> PostsObject { get; set; }
public IEnumerable<Album> ThreadsObject { get; set; }
}
We already know they're objects, so there's no need to add that on the end. This is more readable:
public class ModelMix
{
public IEnumerable<Posts> Posts { get; set; }
public IEnumerable<Album> Threads { get; set; }
}
You need to iterate them like this:
#model MvcMusicStore.Models.ModelMix
<h2>Index</h2>
#for(var i=0; i<model.PostsObject.Count(); i++)
{
#Html.DisplayNameFor(model => model.PostsObject[i].PostContent)
}
And also it's better to save IList instead of IEnumerable, as it will have Count property, instead of using Count() method
Typically you'll need to iterate through each item if you're passing in any type of IEnumerable<>. Since you built a semi-complex model, you'll want to display foreach item in each list. Here is an example based from the ASP.NET MVC tutorials that I think would help you a bit:
#model IEnumerable<ContosoUniversity.Models.Course>
#{
ViewBag.Title = "Courses";
}
<h2>Courses</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.CourseID)
</th>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.Credits)
</th>
<th>
Department
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.CourseID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Credits)
</td>
<td>
#Html.DisplayFor(modelItem => item.Department.Name)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.CourseID }) |
#Html.ActionLink("Details", "Details", new { id=item.CourseID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.CourseID })
</td>
</tr>
}
</table>
Generally most people use an ICollection for their lists, where an example might be in an object:
public virtual ICollection<Post> Posts { get; set; }
Source:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/reading-related-data-with-the-entity-framework-in-an-asp-net-mvc-application
I would recommend starting at the beginning as it'll help you understand why you need to do this:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc
I've researched this a bit and haven't found an answer that quite deals with a similar situation or MVC3. In the ViewModel I'm using I have a Lists of a separate model (List<AgentId> which is a list of the AgentId model).
In the Create page for this controller, I need an input section for 5 items to be added to this list. However, before the page even load, I receive this error message:
There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'BankListAgentId[0].StateCode'.
Here is the ViewModel I am using:
public class BankListViewModel
{
public int ID { get; set; }
public string ContentTypeID1 { get; set; }
public string CreatedBy { get; set; }
public string MANonresBizNY { get; set; }
public string LastChangeOperator { get; set; }
public Nullable<System.DateTime> LastChangeDate { get; set; }
public List<BankListAgentId> BankListAgentId { get; set; }
public List<BankListStateCode> BankListStateCode { get; set; }
}
And here is the section of the view that's having the issues:
<fieldset>
<legend>Stat(s) Fixed</legend>
<table>
<th>State Code</th>
<th>Agent ID</th>
<th></th>
<tr>
<td>
#Html.DropDownListFor(model => model.BankListAgentId[0].StateCode,
(SelectList)ViewBag.StateCode, " ")
</td>
<td>
#Html.EditorFor(model => model.BankListAgentId[0].AgentId)
#Html.ValidationMessageFor(model => model.BankListAgentId[0].AgentId)
</td>
</tr>
<tr>
<td>
#Html.DropDownListFor(model => model.BankListAgentId[1].StateCode,
(SelectList)ViewBag.StateCode, " ")
</td>
<td>
#Html.EditorFor(model => model.BankListAgentId[1].AgentId)
#Html.ValidationMessageFor(model => model.BankListAgentId[1].AgentId)
</td>
<td id="plus2" class="more" onclick="MoreCompanies('3');">+</td>
</tr>
</table>
</fieldset>
I believe #Html.DropDownListFor() is expecting an IEnumerable<SelectListItem>, you can bind it the following way:
In your ViewModel:
public class BankListViewModel
{
public string StateCode { get; set; }
[Display(Name = "State Code")]
public IEnumerable<SelectListItem> BankListStateCode { get; set; }
// ... other properties here
}
In your Controller load the data:
[HttpGet]
public ActionResult Create()
{
var model = new BankListViewModel()
{
// load the values from a datasource of your choice, this one here is manual ...
BankListStateCode = new List<SelectListItem>
{
new SelectListItem
{
Selected = false,
Text ="Oh well...",
Value="1"
}
}
};
return View("Create", model);
}
And then in the View bind it:
#Html.LabelFor(model => model.BankListStateCode)
#Html.DropDownListFor(model => model.StateCode, Model.BankListStateCode)
I hope this helps. Let me know if you nee clarifications.
This error wound up being thrown because the ViewBag element I was using had the same name as one of the list item properties.
The solution was to change ViewBag.StateCode to ViewBag.StateCodeList.