I am very new to .NET MVC and trying to learn MVC. I know that I am doing total wrong here, so I need your help. What I try to do is listing a set of 10 companies, then for each of those company listing the contacts based on the companyID. Please assume that the Entitites and DbContext are set properly, just the problem is between Controller and View is where I couldn't figure out how to:
Here is my Model:
namespace ERP.Models
{
[Table("ERP_Company")]
public class ERP_Company
{
[Key]
public int CompanyID { get; set; }
public string Name { get; set; }
}
[Table("ERP_CompanyContact")]
public class ERP_Contact
{
[Key]
public int ContactID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int CompanyID { get; set; }
}
}
The methods for getting Company and Contact list from the database:
namespace ERP.Models
{
public class Method1
{
private ERPEntities db = new ERPEntities();
public List<ERP_Company> getCompanyList()
{
List<ERP_Company> companyList = (
from c in db.ERP_Company
where c.Name.Contains("Network")
select c).Take(10).ToList();
return companyList;
}
public List<ERP_Contact> getContactList(int CompanyID)
{
List<ERP_Contact> contactList = (
from cc in db.ERP_CompanyContact
where cc.CompanyID == CompanyID
select cc).Take(50).ToList();
return contactList;
}
}
}
Here is my controller where I am doing wrong:
namespace ERP.Controllers
{
public class Test1Controller : Controller
{
//private ERPEntities db = new ERPEntities();
Method1 _repository = new Method1();
public ActionResult Index()
{
ViewData["Company"] = _repository.getCompanyList();
ViewData["Contact"] = _repository.getContactList(CompanyID); // <-- Incorrect Here, but just to show that I want to pass the CompanyID
return View();
}
}
}
Lastly, the View which I want to list the Company, then query all contacts based on CompanyID and list them.
<ul>
#foreach (var item in ViewData["Company"] as List <ERP.Models.ERP_Company>
)
{
<li>#item.CompanyID | #item.Name</li>
<!-- Here is an EXAMPLE that I want to QUERY the Contact recordset and list all the contacts based on the CompanyID -->
<ul>
#for (var i = 0; i < 5; i++)
{<li>Contact #i</li>}
</ul>
}
</ul>
Is it possible to loop through the Contact model (or recordset) within a loop? How can I accomplish this?
Thanks in advance,
Here is how I would implement your case, not in a best way but in a simple way.
Entities:
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Contact> Contacts { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int CompanyId { get; set; }
}
Service:
public class CompanyService
{
public List<Company> getCompanyList()
{
using (ERPEntities db = new ERPEntities())
{
return db.Companies
.Include("Contacts")
.Where(e => e.Name.Contains("Network"))
.Take(10)
.ToList();
}
}
}
Controller:
public HomeController(CompanyService companyService)
{
this.companyService = companyService;
}
public ActionResult Index()
{
List<Company> companies = this.companyService.getCompanyList();
return View(companies);
}
View:
<ul>
#foreach (var company in Model)
{
<li>#company.Id | #company.Name</li>
if (company.Contacts.Count > 0)
{
<ul>
#foreach (var contact in company.Contacts)
{
<li>#contact.FirstName</li>
}
</ul>
}
}
</ul>
In other hand, judging from your implementation, I feel like you may need to work more on fundamental skills like data structure, C#/OOP fundamental then ASP.NET MVC in respective order.
Related
I'm new to asp.net and may be missing some simple solution. So basically i've got two tables Movie and Actor here are their models:
public class Movie
{
public Movie()
{
this.Actors = new HashSet<Actor>();
}
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Genre { get; set; }
[Required]
public DateTime ReleaseDate { get; set; }
[Required]
public DateTime AddDate { get; set; }
public virtual ICollection<Actor> Actors { get; set; }
}
public class Actor
{
public Actor()
{
this.Movies = new HashSet<Movie>();
}
public int id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Movie> Movies { get; set; }
public DateTime BirthDate { get; set; }
}
I'm trying to load movie details and the list of actors from the movie using ViewModel:
public class MovieDetailsViewModel
{
public Movie Movie { get; set; }
public Actor Actor { get; set; }
}
And the controller for it is here:
private ApplicationDbContext _context;
public MoviesController()
{
_context = new ApplicationDbContext();
}
protected override void Dispose(bool disposing)
{
_context.Dispose();
}
public ActionResult Index()
{
var movie = _context.Movies.ToList();
return View(movie);
}
public ActionResult Details(int id)
{
var movie = _context.Movies.SingleOrDefault(m => m.Id == id);
var actor = _context.Actors.SingleOrDefault(a => a.id == id);
var viewModel = new MovieDetailsViewModel
{
Movie = movie,
Actor = actor
};
if (movie == null)
return HttpNotFound();
return View(viewModel);
}
Action Details is the one that needs to be loading the data that I need.
View looks something like this:
#model Movie_Rentals.ViewModels.MovieDetailsViewModel
#{
ViewBag.Title = Model.Movie.Name;
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>#Model.Movie.Name</h2>
<ul>
<li>Genre: #Model.Movie.Genre</li>
<li>Release date: #Model.Movie.ReleaseDate.ToShortDateString()</li>
<li>Add date: #Model.Movie.AddDate.ToShortDateString()</li>
<li>Actors:#Model.Movie.Actors</li>
</ul>
And it displays Actors:System.Collections.Generic.HashSet`1[Movie_Rentals.Models.Actor] which I suppose it should, but I can't mannge to find a solution on how to make it to display the actual list of actors. Does anyone have any idea on my issue, I would appreciate any help.
P.S. I'm not a native english speaker, so sorry if my english is bad.
in your view you could try:
<li>Actors: #string.Join("," Model.Movie.Actors.Select(a => a.Name).ToList())</li>
In this way, you will show a list of actors' names associated with the movie you added to your view model.
But I would suggest you refactor your view model to include a List of actors' names, so you would remove the logic from your view. After that, your view should look like:
public class MovieDetailsViewModel
{
public Movie Movie { get; set; }
public List<string> ActorsNames { get; set; }
}
In your controller you should do:
public ActionResult Details(int id)
{
var movie = _context.Movies.SingleOrDefault(m => m.Id == id);
var viewModel = new MovieDetailsViewModel
{
Movie = movie,
ActorsNames = string.Join(",", movie.Actors.Select(a => a.Name).ToList())
};
if (movie == null)
return HttpNotFound();
return View(viewModel);
}
As you can see, I have removed the code where you get a single actor from the Actors DbSet because it isn't useful. Indeed, you want the full list of all actors that have appeared in the movie you get from the Movies DbSet, not just an actor that has the same id of the movie of which you have to load the details.
Also, be sure to have activated lazy loading to get the collection of actors associated with the movie, otherwise, you will get an empty collection.
Let me know if my suggestions will be useful.
You need to load the Movie with all Actors using
_context.Movies.Include(x => x.Actors).SingleOrDefault(m => m.Id == id);
Then in the View
#foreach (var x in Model.Movie.Actors)
{<li>x.Name</li>}
To allow for the Actors field to show, you can handle this in a few ways. The easiest solution would be to add a property that parses the Actors collection into a string. You could also write code directly into the View to loop over each actor in the collection.
Example:
public class Movie
{
public Movie()
{
this.Actors = new HashSet<Actor>();
}
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Genre { get; set; }
[Required]
public DateTime ReleaseDate { get; set; }
[Required]
public DateTime AddDate { get; set; }
public virtual ICollection<Actor> Actors { get; set; }
public string ActorsToString
{
get
{
string _resultSet = "";
if (Actors != null && Actors.Count > 0)
{
foreach (Actor _actor in Actors)
{
_resultSet += $"{_actor.Name}, ";
}
}
return _resultSet;
}
}
}
I'm using ASP.NET Core 2.2. I have 2 models and a viewmodel which injects data to a view. I want to order results based on their productType. Let's make it clear
This is Product model
public class Product
{
public int ProductID { get; set; }
public int ProductName { get; set; }
public string ProductImage { get; set; }
public int ProductTypeID { get; set; }
[ForeignKey("ProductTypeID")]
public virtual ProductType ProductType{ get; set; }
}
This is ProductType model
public class ProductType
{
public int ProductTypeID { get; set; }
public int ProductTypeName { get; set; }
public string ProductTypeImage { get; set; }
public string ProductTypeDescription { get; set;
}
And finally this is DishesViewModel
public class DishesVM
{
public IEnumerable<ProductType> ProductType { get; set; }
public IEnumerable<Product> Product { get; set; }
}
In MyController I get data from DB then with automapper, map them to DishViewModel
public class HomeController : Controller
{
public IActionResult Dishes()
{
var productTypes= _context.ProductType.OrderBy(p =>p.ProductTypeID).ToList();
var products= _context.Products.OrderBy(p => p.ProductID).ToList();
var DishesVM = new DishesVM();
DishesVM.ProductType = _mapper.Map<IEnumerable<ProductType>>(productTypes);
DishesVM.Product = _mapper.Map<IEnumerable<Product>>(products);
}
}
Now in Dishes View I can have nested foreach
#model DishesViewModel
<div>
foreach(var pt in Model.ProductType)
{
<h1>pt.ProductTypeName</h1>
foreach(var p in Model.Product)
{
p.ProductName
}
}
</div>
This works fine but the only problem it has, is it returns all products. but I want each Product Category has its Products In front of its header. This is visual representation of what I want and what I have now.
This is what I want
But this is what I have
You have to filter your products by product type in each iteration. At the moment you just display all products for each product type:
<div>
foreach(var type in Model.ProductType)
{
//products which belong to the particular type
var productsForType = Model.Product.Where(x => x.ProductTypeID == type.ProductTypeID);
<h1>pt.ProductTypeName</h1>
foreach(var product in productsForType)
{
product.ProductName
}
}
</div>
This will give you a jump on the linq statement:
var list = productTypes.Where(x => x.ProductTypeID == 1).Select(x => new Product()
{
ProductImage = x.ProductTypeImage,
}).ToList();
You will need to decide on what to put into the where clause, I'm using ProductTypeID
I used scaffolding to create the Index, Details, Create, Edit and Delete views and the controller. I have two view models (Parent / Child) relation. In my Index view I want to display the list of Teams as well as some information on the players (Parent / child). For example I want to display in the Index view the teams with the players count per team and last players that was modified. I am not sure where to begin.
Example:
(Team) Red -- (Last Modified) 01/02/2015 -- (Number Players) 10 and so on.
Team ViewModel
public class TeamVM
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime? LastActivity { get; set; }
public string NumberPlayers { get; set; }
public IList<PLayerVM> PlayerVM { get; set; }
}
Player ViewModel
public class PlayerVM
{
public int ID { get; set; }
public int TeamID { get; set; }
public string PlayerInfo { get; set; }
public DateTime? CreateDate { get; set; }
}
Other ViewModel
public class TeamViewModel
{
public List<Team> Teams{ get; set; }
}
Controller
public ActionResult Index()
{
TeamViewModelviewModel = new TeamViewModel();
viewModel.Teams= db.Teams.ToList();
return View(viewModel);
}
db.Products.ToList()?? I assume that is where you mean db.Teams.ToList()?
You are using viewmodels, so you should map the db data to your viewmodels first:
public ActionResult Index()
{
var teams = db
.Teams
.Include("Players") // Assuming your Team entity has a collection of Players
.SelectMany(t => new TeamVM {
ID = t.ID,
// etc..
})
.ToList();
return View(new TeamViewModel { Teams = teams });
}
model:
public class TeamVM
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime? LastActivity { get; set; }
public IList<PLayerVM> PlayerVM { get; set; }
public int NumberPlayers {
get { return PlayerVM.Count(); }
}
}
Then in your view:
#model MyProject.Models.TeamViewModel
<table>
#foreach(var team in Model.Teams.ToList()) {
<tr>
<td>#team.Name</td> // Name
<td>#team.NumberPlayers</td> // Playercount
<td>#team.PlayerVM.Max(p => p.LastActivity).LastActivity</td> // Last edited
</tr>
}
</table>
I currently have a navigation bar which is dynamically created from a MS SQL Database, but I would like to add a second sub layer. Below is what I have so far for the current working single level navigation bar.
Navigation
public partial class Navigation
{
public int Id { get; set; }
public string Title { get; set; }
public Nullable<int> Position { get; set; }
public bool Main { get; set; }
public string Action { get; set; }
public string Controller { get; set; }
}
HomeController
[ChildActionOnly]
public ActionResult Navigation()
{
var navigationModel = (from m in db.Navigations where (m.Main == true) orderby m.Position select m);
return View(navigationModel);
}
Navigation
#{ Layout = null; }
#model IEnumerable<WebApplication1.Models.Navigation>
<ul class="nav sf-menu clearfix">
#foreach (var item in Model)
{
#Html.MenuLink(item.Title, item.Action, item.Controller)
}
</ul>
The second tier needs to link to the Contents table, the relationship is db.Navigations.Id = db.Contents.NavigationId.
Content
public partial class Content
{
public int Id { get; set; }
public Nullable<int> NavigationId { get; set; }
public string Title { get; set; }
public string Content1 { get; set; }
public Nullable<int> Position { get; set; }
public string Image { get; set; }
public string Sub { get; set; }
public Nullable<bool> Active { get; set; }
public string Url { get; set; }
public string Summary { get; set; }
}
Could someone please provide a tutorial or example code of how I can bind child data to the parent dataset and then in the View, how to check if a parent holds child records and if so loop through them.
Any help would be much appreciated :-)
I added a new class called NavigationViewModel to declare the tables I wish to use and modified the HomeController to use NavigationViewModel for querying the respective tables.
NavigationViewModel
namespace WebApplication1.Models
{
public class NavigationViewModel
{
public List<Navigation> Navigation { get; set; }
public List<Content> Content { get; set; }
}
}
HomeController
[ChildActionOnly]
public ActionResult Navigation()
{
var navigationModel = new NavigationViewModel();
navigationModel.Navigation = (from m in db.Navigations where (m.Main == true) orderby m.Position select m).ToList();
navigationModel.Content = (from n in db.Contents where (n.Active == true) orderby n.Position select n).ToList();
return View(navigationModel);
}
Navigation
#model WebApplication1.Models.NavigationViewModel
<ul class="nav sf-menu clearfix">
#foreach (var navigation in Model.Navigation)
{
int records = Model.Content.Count(c => c.NavigationId == navigation.Id);
if (records > 0)
{
#Html.SubMenuLink(navigation.Title, navigation.Action, navigation.Controller)
#Html.Raw("<ul>")
foreach (var content in Model.Content.Where(c => c.NavigationId == navigation.Id))
{
if (string.IsNullOrEmpty(content.Url))
{
if (string.IsNullOrEmpty(content.Content1))
{
}
else
{
#Html.MenuLink(content.Title, "Home/Article/" + content.Id + "/" + ToFriendlyUrl(content.Title), "Home");
}
}
else
{
#Html.MenuLink(content.Title, content.Url, "Home");
}
}
#Html.Raw("</ul>")
#Html.Raw("</li>")
}
else
{
#Html.MenuLink(navigation.Title, navigation.Action, navigation.Controller)
}
}
</ul>
I'm not sure this is the most elegant or efficient way to achieve the end goal, any suggestions to make the code more efficient I am happy to listen.
I have a viewmodel which needs data from two models person and address:
Models:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public int Gender { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public int Zip { get; set; }
public int PersonId {get; set; }
}
The Viewmodel is as such
public class PersonAddViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Street { get; set; }
}
I have tried several ways to get data into the viewmodel and pass it to the view. There will be multiple records returned to display.
My latest method is populating the view model as such:
private AppContexts db = new AppContexts();
public ActionResult ListPeople()
{
var model = new PersonAddViewModel();
var people = db.Persons;
foreach(Person p in people)
{
Address address = db.Addresses.SingleOrDefault(a => a.PersonId == p.Id)
model.Id = p.Id;
model.Name = p.Name;
model.Street = address.Street;
}
return View(model.ToList());
}
I get an error on the Address address = db... line of "EntityCommandExecutionException was unhandled by user code.
How can you populate a view model with multiple records and pass to a view?
Final Solution:
private AppContexts db = new AppContexts();
private AppContexts dbt = new AppContexts();
public ActionResult ListPeople()
{
List<PersonAddViewModel> list = new List<PersonAddViewModel>();
var people = db.Persons;
foreach(Person p in people)
{
PersonAddViewModel model = new PersonAddViewModel();
Address address = dbt.Addresses.SingleOrDefault(a => a.PersonId == p.Id)
model.Id = p.Id;
model.Name = p.Name;
model.Street = address.Street;
}
return View(list);
}
First, EntityCommandExecutionException errors indicates an error in the definition of your entity context, or the entities themselves. This is throwing an exception because it's found the database to be different from the way you told it that it should be. You need to figure out that problem.
Second, regarding the proper way to do this, the code you've shown should work if your context were correctly configured. But, a better way would be to use Navigational properties, so long as you want to get all related records and not specify other Where clause parameters. A navigational property might look like this:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public int Gender { get; set; }
public virtual Address Address { get; set; }
// or possibly, if you want more than one address per person
public virtual ICollection<Address> Addresses { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public int Zip { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
Then you would simply say:
public ActionResult ListPeople()
{
var model = (from p in db.Persons // .Includes("Addresses") here?
select new PersonAddViewModel() {
Id = p.Id,
Name = p.Name,
Street = p.Address.Street,
// or if collection
Street2 = p.Addresses.Select(a => a.Street).FirstOrDefault()
});
return View(model.ToList());
}
For displaying lists of objects, you could use a generic view model that has a generic list:
public class GenericViewModel<T>
{
public List<T> Results { get; set; }
public GenericViewModel()
{
this.Results = new List<T>();
}
}
Have a controller action that returns, say all people from your database:
[HttpGet]
public ActionResult GetAllPeople(GenericViewModel<People> viewModel)
{
var query = (from x in db.People select x); // Select all people
viewModel.Results = query.ToList();
return View("_MyView", viewModel);
}
Then make your view strongly typed, taking in your generic view model:
#model NameSpace.ViewModels.GenericViewModel<NameSpace.Models.People>